<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Zapotek&#039;s train of thought...</title>
	<atom:link href="http://trainofthought.segfault.gr/feed/" rel="self" type="application/rss+xml" />
	<link>http://trainofthought.segfault.gr</link>
	<description></description>
	<lastBuildDate>Sat, 27 Apr 2013 23:08:05 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<item>
		<title>Ruby: Substitution with grouped regular expressions</title>
		<link>http://trainofthought.segfault.gr/2012/12/09/ruby-substitution-with-grouped-regular-expressions/</link>
		<comments>http://trainofthought.segfault.gr/2012/12/09/ruby-substitution-with-grouped-regular-expressions/#comments</comments>
		<pubDate>Sun, 09 Dec 2012 00:19:22 +0000</pubDate>
		<dc:creator>Zapotek</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[grouped regular expressions]]></category>
		<category><![CDATA[regular expression]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[string]]></category>
		<category><![CDATA[sub]]></category>
		<category><![CDATA[substitue]]></category>
		<category><![CDATA[substitution]]></category>

		<guid isPermaLink="false">http://trainofthought.segfault.gr/?p=2162</guid>
		<description><![CDATA[EDIT: Replaced &#8220;String#sub_in_groups&#8221; method with a context-conscious one. Hey guys. It&#8217;s been a while since I posted here, been quite busy with my new job at Rapid7 and working on Arachni but I wanted to share a little piece of code with you. Like with the last post, I was working on something for Arachni [...]]]></description>
				<content:encoded><![CDATA[<p>EDIT: Replaced &#8220;String#sub_in_groups&#8221; method with a <a href="http://stackoverflow.com/a/13787676/1889337">context-conscious one</a>.</p>
<p>Hey guys.</p>
<p>It&#8217;s been a while since I posted here, been quite busy with my new job at Rapid7 and working on Arachni but I wanted to share a little piece of code with you.</p>
<p>Like with the last post, I was working on something for Arachni and since it took me a few hours of googling before I gave up and rolled my own I figured I&#8217;d post the solution here and save someone else some time.</p>
<p>What I was working on is the first step of URL rewrite support and the first issue was allowing the user to specify how paths should be interpreted.<br />
That was easy, have the user specify regular expressions, and to make things a bit nicer/cleaner/neater have them specify named groups for each parameter embedded in the path.</p>
<p>So say we&#8217;ve got this path: <pre class="crayon-plain-tag">/book/12/blahahaha/test/chapter-3/stuff4/12</pre></p>
<p>The book category and its ID, then we&#8217;ve got some random crap that are to be ignored, then the chapter ID then some other ID and then a value identical to the book ID.</p>
<p>So an appropriate regexp to extract the data we need would be:</p><pre class="crayon-plain-tag">/
    \/(?<category>\w+)         # matches category type
    \/                         # path separator
    (?<book-id>\d+)            # matches book ID numbers
    \/                         # path separator
    .*                         # irrelevant
    \/                         # path separator
    chapter-(?<chapter-id>\d+) # matches chapter ID numbers
    \/                         # path separator
    stuff(?<stuff-id>\d+)      # matches stuff ID numbers
/x</pre><p></p>
<p>It would be nice if we could get the matches as a hash with the group name as the key and the matched data as the value like so:</p>
<p></p><pre class="crayon-plain-tag">{
      "category" => [ "book" ],
       "book-id" => [ "12" ],
    "chapter-id" => [ "3" ],
      "stuff-id" => [ "4" ]
}</pre><p></p>
<p>(The values are arrays in case the regexp matches more that one value. In the case of extracting URL rewrite data the values should be unique and singular but it&#8217;s nice to have a smarter algo to cover future needs.)</p>
<p>We can do the above with:</p><pre class="crayon-plain-tag">class String
    def scan_in_groups( regexp )
        raise ArgumentError, 'Regexp does not contain any names.' if regexp.names.empty?

        captures = regexp.names.inject( {} ){ |h, n| h[n] = []; h }

        scan( regexp ).each do |match|
            captures.keys.zip( match ).each do |group, gmatch|
                next if !gmatch
                captures[group] << gmatch
            end
        end

        captures.reject { |_, v| v.empty? }
    end
end

regexp = /
    \/(?<category>\w+)         # matches category type
    \/                         # path separator
    (?<book-id>\d+)            # matches book ID numbers
    \/                         # path separator
    .*                         # irrelevant
    \/                         # path separator
    chapter-(?<chapter-id>\d+) # matches chapter ID numbers
    \/                         # path separator
    stuff(?<stuff-id>\d+)      # matches stuff ID numbers
/x

path = '/book/12/blahahaha/test/chapter-3/stuff4/12'

p path.scan_in_groups( regexp )
#=> {"category"=>["book"], "book-id"=>["12"], "chapter-id"=>["3"], "stuff-id"=>["4"]}</pre><p></p>
<p>It&#8217;d also be nice if we could substitute the matched named groups with a hash similar to the one returned by #scan_in_groups &#8212; this would be used to make fuzzing the inputs in the path easier.<br />
I was sure that String#sub would provide a way of doing this (since it supports grouped regular expressions) but, unfortunately, it does not; however it&#8217;s not a big deal since it only takes a few lines of code.</p>
<p></p><pre class="crayon-plain-tag">class String

    def scan_in_groups( regexp )
        raise ArgumentError, 'Regexp does not contain any names.' if regexp.names.empty?

        captures = regexp.names.inject( {} ){ |h, n| h[n] = []; h }

        scan( regexp ).each do |match|
            captures.keys.zip( match ).each do |group, gmatch|
                next if !gmatch
                captures[group] << gmatch
            end
        end

        captures.reject { |_, v| v.empty? }
    end

    def sub_in_groups( regexp, group_hash )
        dup.sub_in_groups!( regexp, group_hash )
    end

    def sub_in_groups!( regexp, updates )
        return if !(match = regexp.match( self ))

        keys_in_order = updates.keys.sort_by { |k| match.offset( k ) }.reverse
        keys_in_order.each do |k|
            offsets_for_group = match.offset( k )
            self[offsets_for_group.first...offsets_for_group.last] = updates[k]
        end

        self
    end


end

regexp = /
    \/(?<category>\w+)         # matches category type
    \/                         # path separator
    (?<book-id>\d+)            # matches book ID numbers
    \/                         # path separator
    .*                         # irrelevant
    \/                         # path separator
    chapter-(?<chapter-id>\d+) # matches chapter ID numbers
    \/                         # path separator
    stuff(?<stuff-id>\d+)      # matches stuff ID numbers
/x

path = '/book/12/blahahaha/test/chapter-3/stuff4/12'

p path.scan_in_groups( regexp )
#=> {"category"=>["book"], "book-id"=>["12"], "chapter-id"=>["3"], "stuff-id"=>["4"]}

update = {
    'category'   => 'new-category',
    'book-id'    => 'new-book-id',
    'chapter-id' => 'new-chapter-id',
    'stuff-id'   => '-new-stuff-id'
}

p path.sub_in_groups( regexp, update )
#=> "/new-category/new-book-id/blahahaha/test/chapter-new-chapter-id/stuff-new-stuff-id/12"</pre><p></p>
<p>Yeah the code is a bit rough but you get the idea&#8230;<br />
So, if you want a way to perform string substitutions with grouped regular expressions using the group names, there you go, look no further.</p>
]]></content:encoded>
			<wfw:commentRss>http://trainofthought.segfault.gr/2012/12/09/ruby-substitution-with-grouped-regular-expressions/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ruby cache implementation</title>
		<link>http://trainofthought.segfault.gr/2012/06/03/ruby-cache-implementation/</link>
		<comments>http://trainofthought.segfault.gr/2012/06/03/ruby-cache-implementation/#comments</comments>
		<pubDate>Sun, 03 Jun 2012 04:50:00 +0000</pubDate>
		<dc:creator>Zapotek</dc:creator>
				<category><![CDATA[Arachni]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Projects]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[algorithm]]></category>
		<category><![CDATA[arachni]]></category>
		<category><![CDATA[cache]]></category>
		<category><![CDATA[caching]]></category>
		<category><![CDATA[cost]]></category>
		<category><![CDATA[crawl]]></category>
		<category><![CDATA[least recently used]]></category>
		<category><![CDATA[random replacement]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[url normalization]]></category>

		<guid isPermaLink="false">http://trainofthought.segfault.gr/?p=2090</guid>
		<description><![CDATA[Those of you who&#8217;ve been following Arachni&#8217;s Twitter account or Blog you&#8217;ll know where this post is coming from. I recently found that my URL normalization methods were sucking up loads of CPU time and that caching these methods (with a simple Ruby Hash) was cutting the time of a 1000-page crawl to almost half; [...]]]></description>
				<content:encoded><![CDATA[<p>Those of you who&#8217;ve been following Arachni&#8217;s <a href="https://twitter.com/#!/ArachniScanner">Twitter account</a> or <a href="http://arachni-scanner.com/blog/massive-crawler-performance-increase">Blog</a> you&#8217;ll know where this post is coming from.</p>
<p>I recently found that my URL normalization methods were sucking up loads of CPU time and that caching these methods (with a simple Ruby Hash) was cutting the time of a 1000-page crawl to almost half; that was a great day as you can imagine, but for the time being I left it at that and continued working on the specs &#8212; which I&#8217;ve now gotten sick of.<br />
After I finished adding specs for all Arachni&#8217;s core classes I went back to add a proper cache, after all I couldn&#8217;t just let these methods infinitely populate their cache hashes.</p>
<p>Never having been in that situation before I took a stroll over at <a href="http://en.wikipedia.org/wiki/Cache_algorithms">Wikipedia</a> to get me started. I soon discovered that what I needed was a <a href="http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used">Least Recently Used</a> cache, thus I did a bit of googling to see if some kind soul had already written such a cache in Ruby. Luckily, I did find a few implementations that seemed to do the job and with those as a base I got cracking.</p>
<p>Sadly, the overhead of having to maintain ages for all entries after each access operation completely negated the performance improvements of caching &#8212; i.e. the crawl time was the same either way. I spent hours trying to write or find a better LRU algorithm but no dice.</p>
<p>So back to that Wikipedia page I went, trying to find some other algorithm that could suite my needs. Then, my eye fell on the <a href="http://en.wikipedia.org/wiki/Cache_algorithms#Random_Replacement">Random Replacement</a> one, which sounded nice. Since it randomly removes cache entries when it needs to make space for new ones there&#8217;s pretty much no overhead, nothing to keep track of &#8212; plus the description has a nice side-note that <em>For its simplicity, it has been used in ARM processors</em> so I figured why not.</p>
<p>Fortunately, it performed <strong><em>beautifully</em></strong>, fast and simple and no overhead.</p>
<p>But, as it usually is, that wasn&#8217;t enough (for me)&#8230; I figured that I should try to push it a bit further and update the RR algo to be cost-inclusive.<br />
That is, each entry would belong to a cost class, (<em>low</em>, <em>medium</em> and <em>high</em>) with the cache giving precedence to entries of the lowest classes when it needs to make room for new entries.</p>
<p>Unfortunately though, the operations in question weren&#8217;t costly enough so as to justify the gains one would enjoy from introducing that tiny cost-checking overhead in the cache; soooo&#8230; no dice.</p>
<p>And this is why I&#8217;m writing this post, if someone out there finds (him|her)self in a similar situation and needs a cache implementation (or 3) you can just use mine and skip all the hassle.</p>
<p>I may have waisted a few hours but I ended up with 3 cache implementations and at least one of them works so overall it wasn&#8217;t too bad.</p>
<p>Here&#8217;s the code of the cache classes: <a href="https://github.com/Arachni/arachni/tree/experimental/lib/arachni/cache">https://github.com/Arachni/arachni/tree/experimental/lib/arachni/cache</a><br />
And their RSpec tests: <a href="https://github.com/Arachni/arachni/tree/experimental/spec/arachni/cache">https://github.com/Arachni/arachni/tree/experimental/spec/arachni/cache</a></p>
]]></content:encoded>
			<wfw:commentRss>http://trainofthought.segfault.gr/2012/06/03/ruby-cache-implementation/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Roundabout &#8211; A high-performance, distributed crawler</title>
		<link>http://trainofthought.segfault.gr/2012/05/16/roundabout-a-high-performance-distributed-crawler/</link>
		<comments>http://trainofthought.segfault.gr/2012/05/16/roundabout-a-high-performance-distributed-crawler/#comments</comments>
		<pubDate>Wed, 16 May 2012 00:24:43 +0000</pubDate>
		<dc:creator>Zapotek</dc:creator>
				<category><![CDATA[Arachni]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Projects]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Web Application]]></category>
		<category><![CDATA[asynchronous]]></category>
		<category><![CDATA[crawl]]></category>
		<category><![CDATA[distributed]]></category>
		<category><![CDATA[grid]]></category>
		<category><![CDATA[high performance]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[spider]]></category>
		<category><![CDATA[workload]]></category>

		<guid isPermaLink="false">http://trainofthought.segfault.gr/?p=2083</guid>
		<description><![CDATA[When I started development on the Arachni high-performance grid my focus was on the audit part, i.e. find a way to distribute the audit of batches of individual elements across multiple nodes and avoid duplication of effort amongst them. It was a bit tricky to get right but it turned out to be quite do-able [...]]]></description>
				<content:encoded><![CDATA[<p>When I started development on the Arachni high-performance grid my focus was on the audit part, i.e. find a way to distribute the audit of batches of individual elements across multiple nodes and avoid duplication of effort amongst them.<br />
It was a bit tricky to get right but it turned out to be quite do-able and worthwhile.</p>
<p>However, the crawl was done the old fashioned way, the master instance would crawl the targeted website and once completed it would then analyze all the pages it found and spread the workload.<br />
I always intended to try out my hand on something similar but aimed towards the crawling process but it wasn&#8217;t a high priority.<br />
But, as you can see from my last post, I did sort of figure it out, although I hadn&#8217;t had a chance to implement it until now.</p>
<p>This is tricky to do because there&#8217;s no way of knowing the workload before hand as it is basically a freaking labyrinth and precious information (new paths) can be hidden behind walls and walls of crap.</p>
<p>On the other hand, since when running Arachni in HPG mode you already have a few nodes up and running in the first place, why not utilize them a bit more &#8212; even if it turns out to be only slightly faster than a single crawler.</p>
<p>With that in mind, I yesterday started to implement that sort of a crawler, and <a href="https://github.com/Zapotek/Roundabout">here it is</a>.<br />
Its sole existence is that of a toy, a fun experiment, and not as a stable system. I may, in the future, put some more effort into it but my main reason for doing this is to explore this idea and eventually port it over to Arachni.</p>
<p>If you find this interesting, want to help out in researching or have any sort of feedback or just want to get in touch don&#8217;t hesitate to do so.</p>
<p>Cheers,<br />
Tasos L.</p>
]]></content:encoded>
			<wfw:commentRss>http://trainofthought.segfault.gr/2012/05/16/roundabout-a-high-performance-distributed-crawler/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Distributing the crawl</title>
		<link>http://trainofthought.segfault.gr/2012/01/19/distributing-the-crawl/</link>
		<comments>http://trainofthought.segfault.gr/2012/01/19/distributing-the-crawl/#comments</comments>
		<pubDate>Thu, 19 Jan 2012 10:06:28 +0000</pubDate>
		<dc:creator>Zapotek</dc:creator>
				<category><![CDATA[Arachni]]></category>
		<category><![CDATA[Projects]]></category>
		<category><![CDATA[arachni]]></category>
		<category><![CDATA[cluster]]></category>
		<category><![CDATA[crawl]]></category>
		<category><![CDATA[distribution]]></category>
		<category><![CDATA[grid]]></category>

		<guid isPermaLink="false">http://trainofthought.segfault.gr/?p=2031</guid>
		<description><![CDATA[This one had been bugging me since I first started work on the HPG. The gain you get from distributed computing is directly related to how efficient the workload distribution is &#8212; which makes sense. The crawling process though doesn&#8217;t consist of a workload per se but rather looks for the workload. Also, the difficulty [...]]]></description>
				<content:encoded><![CDATA[<p>This one had been bugging me since I first started work on the HPG.<br />
The gain you get from distributed computing is directly related to how efficient the workload distribution is &#8212; which makes sense.<br />
The crawling process though doesn&#8217;t consist of a <em>workload</em> per se but rather <em>looks</em> for the workload.<br />
Also, the difficulty of the crawl doesn&#8217;t lie in parsing or following the paths but actually finding the paths, this is because new paths are hidden behind old ones and as you progress new paths become sparser and sparser.<br />
So the more work you do the less productive you become &#8212; these are grim prospects.</p>
<p>So here&#8217;s our problem, how the hell do you distribute something when you don&#8217;t know:</p>
<ul>
<li>where it is</li>
<li>what it is</li>
<li>how big it is</li>
</ul>
<p>Truthfully, in a single node setup this can be done quite easily and there are lots of ways to go about it<br />
A basic crawling algorithm is actually one of the simplest around, it only has one rule: <em>Follow <strong>every</strong> path but <strong>only once</strong> &#8212; when there are no more new paths you&#8217;re done.</em></p>
<p>In the most basic of implementations what you&#8217;d need to do this is a look-up table to help you keep track of the paths you&#8217;ve already followed in order not to go over them again.<br />
Or you can amend the previous model by going the multi-threaded way and put the new paths in a queue, have workers pop paths from the queue, follow them and then report the new paths they find back to their coordinator for de-duplication and then put everything that passes filtering back to the queue and so the story goes&#8230;</p>
<p>When you do this on a single machine these approaches are good enough (and when you use async requests for the first/simplest approach it becomes more efficient than the latter one as well).<br />
Thing is, these work well because you have the benefit of a multi Gbps bandwidth and close-to-zero latency pipe, your Front-Side Bus (or whatever computers have nowadays, haven&#8217;t kept up with h/w design).</p>
<p>So you can check if something is in the lookup table in something close to 0s, actually in Ruby the look-up time of a Set with 1,000,000 URLs in it is <em>9.735e-06</em> (<em>0.000009735</em>) seconds on my machine.<br />
Which is effectively no time at all, you spend <em>0.000009735</em> seconds waiting for a decision before following each URL &#8212; ooooh scary.</p>
<p>However, when you need to do this over the network these response times take a dive &#8212; off a cliff.<br />
You see, when distributing work you play with gain/cost ratios; if the ratio is good you go ahead, if not you go back to the drawing board.</p>
<p>Such a naive implementation deserves picking up the multi-colored markers again because:</p>
<p>Let&#8217;s say every worker communicates via an RPC protocol and the master worker maintains the look-up table and the work Queue.<br />
Assuming that an RPC call costs about the same as an HTTP request and 25% of the paths contained in most pages are identical (nav menu, css, js, images, links to new blog posts etc.) this means that each <em>follow</em> operation will cost, per work unit:<br />
1 RPC call to pop a path from the Queue +</p>
<ul>
<li>25% of the amount of links found by following the path * 1 RPC &#8212; for paths that are common and have already been visited from the get go</li>
<li>75% of the amount of links found by following the path * 1 RPC call + 1 HTTP request &#8212; for new paths which aren&#8217;t in the lookup table and must be visited</li>
</ul>
<p>So you&#8217;ll be spending most of your time waiting for permission rather than doing actual work and suddenly the cost of doing the work doubles.<br />
There has to be a better way&#8230;</p>
<p>Annoyingly, it took me a couple of days to figure this out and it turned out what I needed was a relaxing shower and the answer come to me on its own &#8212; <em>gooood answer *pat**pat*</em>.</p>
<p>In all honesty, I had bits and pieces of the answer from the begging and I knew that the final algorithm would have to be a composite of models &#8212; a piece from the producer-consumer there, a bit of a policy-enforcer here sprinkled with some delegation across the board &#8212; but the problem was putting them in the right order to form a unified model that would:</p>
<ul>
<li>Avoid any sort of blocking (no look-ups or waiting for decisions)</li>
<li>Automate load balancing</li>
<li>Prevent crawling redundant URLs (more than one worker following the same path)</li>
</ul>
<p>Tricky stuff right?</p>
<p>And here&#8217;s what I came up with:</p>
<ol>
<li>The master scopes out the place (follows 10 paths or so) and deduces the webapp structure &#8212; it will, most certainly, be incomplete but it doesn&#8217;t matter as we just want some seeds to get us going.</li>
<li>The master creates a per directory policy and assigns dirs to workers AND sends that policy to them as well.</li>
<li>Workers perform the crawl as usual but also implement that policy for URLs that don&#8217;t match their own policy rules i.e. send URLs that are out of their scope to the appropriate peer and let him handle it &#8212; the peer will ignore it if he has already visited it or put it in his queue.</li>
<li>If no policy matches a URL then it is sent back to the master; the master creates a new policy(ies), stores the work in a Queue and then sends an announcement to the workers (&#8220;There&#8217;s some work up for grabs!&#8221; ).</li>
<li>Busy workers ignore it; idling workers try to pull it and the work is assigned first-come/first-serve along with the updated policy.</li>
<li>Go to 3</li>
</ol>
<p>If at any point a worker becomes idle he sends the paths he has discovered back to the master for storage/further processing/whatever and tries to pull some new work.</p>
<p>Also, the master will be a worker too &#8212; why waste a node, right?</p>
<p>Let&#8217;s go back to our list of requirements and see how we did:</p>
<ul>
<li>Avoid any sort of blocking (no look-ups or waiting for decisions) &#8212; <strong>✓</strong> Instead of waiting for permission to do something, we delegate to the appropriate authority and forget about it</li>
<li>Automate load balancing &#8212; <strong>✓</strong> Workers pull work when they are good and ready</li>
<li>Prevent crawling redundant URLs (no more than one worker following the same path) &#8212; <strong>✓</strong> A local look-up table and item #1 take care of that quite nicely</li>
</ul>
<p>In addition, the policy can use any sort of criteria (directories were just an example), which means that we can achieve very granular distribution if we are a bit clever with it.</p>
<p>I think that this serves as a decent starting point, there&#8217;s still the issue of how to efficiently group the new URLs that are fed back to the master (because they don&#8217;t match any of the initial policy rules) but I&#8217;ll have to see this working under real world conditions in order to get a better feel for it first.<br />
I&#8217;ve got some stuff in mind, we&#8217;ll see&#8230;</p>
<p>Please do comment if you have a suggestion of have spotted a fault somewhere.</p>
<p> &#8211; Tasos</p>
]]></content:encoded>
			<wfw:commentRss>http://trainofthought.segfault.gr/2012/01/19/distributing-the-crawl/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Arachni is moving away from GPLv2 and towards Apache License v2.0</title>
		<link>http://trainofthought.segfault.gr/2012/01/12/arachni-is-moving-away-from-gplv2-and-towards-apache-license-v2-0/</link>
		<comments>http://trainofthought.segfault.gr/2012/01/12/arachni-is-moving-away-from-gplv2-and-towards-apache-license-v2-0/#comments</comments>
		<pubDate>Thu, 12 Jan 2012 15:40:22 +0000</pubDate>
		<dc:creator>Zapotek</dc:creator>
				<category><![CDATA[Arachni]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[Projects]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Web Application]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[apache license 2.0]]></category>
		<category><![CDATA[arachni]]></category>
		<category><![CDATA[asf]]></category>
		<category><![CDATA[gpl]]></category>
		<category><![CDATA[gplv2]]></category>

		<guid isPermaLink="false">http://trainofthought.segfault.gr/?p=2012</guid>
		<description><![CDATA[Yes, it&#8217;s true&#8230; As of now the code in the experimental branch has been converted to use the Apache License Version 2.0. If you&#8217;re interested in why this happened here&#8217;s the deal: There are currently a few companies that use Arachni internally and a few others that actually provide SaaS security services using Arachni&#8217;s distributed [...]]]></description>
				<content:encoded><![CDATA[<p>Yes, it&#8217;s true&#8230;<br />
As of now the code in the <a href="https://github.com/Arachni/arachni/tree/experimental">experimental branch</a> has been converted to use the Apache License Version 2.0.</p>
<p>If you&#8217;re interested in why this happened here&#8217;s the deal:<br />
There are currently a few companies that use Arachni internally and a few others that actually provide SaaS security services using Arachni&#8217;s distributed features.<br />
Thing is though, a lot of companies can&#8217;t touch GPL code (not that I blame them) which isn&#8217;t good for them nor Arachni as neither of us gets what he wants.<br />
It makes sense, surely, but it was about a month ago that it really clicked as I were reading the comments of a Slashdot article.<br />
Lots of people were agreeing on the same subject, the money-men don&#8217;t like GPL which kind of sucked for the project.</p>
<p>At this point I started researching alternative licenses and started asking around a bit.</p>
<p>As fate would have it &#8212; although more due to the increasing userbase I guess &#8212; a few people told me that the GPL was a deal-braker for them and I even had one guy tell me that he couldn&#8217;t include Arachni in his book because of it (I&#8217;ll spare him his blushes and not say his name).</p>
<p>Now that the project is gaining some momentum these technicalities become more and more important.</p>
<p>So after a bit of research I settled on Apache License 2.0 mainly because of its trademark and patent grants (because who the hell wants to deal with that bureaucratic crap?) and the requirement to redistribute a visible copy of the original work&#8217;s <em>NOTICE</em> file (if it includes one) which is nice since hard work must be properly credited.<br />
You know, you can use my work for free (and I hope you do <img src='http://trainofthought.segfault.gr/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  ) but mention that &#8220;this product contains some code from that Arachni thingy written by this bloke with the funny name&#8221;.</p>
<p>So that&#8217;s the reason, I&#8217;m hoping that a more permissive license will increase adoption and make everybody&#8217;s life easier.</p>
]]></content:encoded>
			<wfw:commentRss>http://trainofthought.segfault.gr/2012/01/12/arachni-is-moving-away-from-gplv2-and-towards-apache-license-v2-0/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Arachni v0.4.0.2 HOTFIX</title>
		<link>http://trainofthought.segfault.gr/2012/01/09/arachni-v0-4-0-2-hotfix/</link>
		<comments>http://trainofthought.segfault.gr/2012/01/09/arachni-v0-4-0-2-hotfix/#comments</comments>
		<pubDate>Mon, 09 Jan 2012 20:23:08 +0000</pubDate>
		<dc:creator>Zapotek</dc:creator>
				<category><![CDATA[Arachni]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Projects]]></category>
		<category><![CDATA[Releases]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[arachni]]></category>
		<category><![CDATA[audit]]></category>
		<category><![CDATA[automated]]></category>
		<category><![CDATA[crawler]]></category>
		<category><![CDATA[framework]]></category>
		<category><![CDATA[open source]]></category>
		<category><![CDATA[plugins]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[scanner]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[vulnerability]]></category>
		<category><![CDATA[vulnerability scanner]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[web application]]></category>
		<category><![CDATA[webapp]]></category>
		<category><![CDATA[xss]]></category>

		<guid isPermaLink="false">http://trainofthought.segfault.gr/?p=2005</guid>
		<description><![CDATA[Hi guys, A couple of days ago I proudly released v0.4 and, as luck would have it, I later had to swallow some of that pride due to a couple of intermittent bugs that I hadn&#8217;t spotted. Well, worry no more as I&#8217;m writing this post to announce a rush hotfix version of Arachni, v0.4.0.2. [...]]]></description>
				<content:encoded><![CDATA[<p>Hi guys,</p>
<p>A couple of days ago I proudly released v0.4 and, as luck would have it, I later had to swallow some of that pride due to a couple of intermittent bugs that I hadn&#8217;t spotted.<br />
Well, worry no more as I&#8217;m writing this post to announce a rush hotfix version of Arachni, v0.4.0.2.</p>
<p>If you installed the previous version via &#8220;gem install&#8221; or have downloaded the previous Cygwin package then all you need to do is issue:<br />
<pre class="crayon-plain-tag">gem install arachni</pre></p>
<p>If not, you can find links and instructions for all available packages at: <a href="http://arachni-scanner.com/latest">http://arachni-scanner.com/latest</a></p>
<p>Enjoy <img src='http://trainofthought.segfault.gr/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://trainofthought.segfault.gr/2012/01/09/arachni-v0-4-0-2-hotfix/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Arachni v0.4 is out</title>
		<link>http://trainofthought.segfault.gr/2012/01/07/arachni-v0-4-is-out/</link>
		<comments>http://trainofthought.segfault.gr/2012/01/07/arachni-v0-4-is-out/#comments</comments>
		<pubDate>Sat, 07 Jan 2012 06:48:43 +0000</pubDate>
		<dc:creator>Zapotek</dc:creator>
				<category><![CDATA[Arachni]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Projects]]></category>
		<category><![CDATA[Releases]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Web Application]]></category>
		<category><![CDATA[arachni]]></category>
		<category><![CDATA[audit]]></category>
		<category><![CDATA[automated]]></category>
		<category><![CDATA[crawler]]></category>
		<category><![CDATA[framework]]></category>
		<category><![CDATA[grid]]></category>
		<category><![CDATA[module]]></category>
		<category><![CDATA[modules]]></category>
		<category><![CDATA[open source]]></category>
		<category><![CDATA[plugins]]></category>
		<category><![CDATA[release]]></category>
		<category><![CDATA[report]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[scanner]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[server]]></category>
		<category><![CDATA[spider]]></category>
		<category><![CDATA[update]]></category>
		<category><![CDATA[v0.4]]></category>
		<category><![CDATA[vulnerability]]></category>
		<category><![CDATA[vulnerability scanner]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[web application]]></category>
		<category><![CDATA[webapp]]></category>
		<category><![CDATA[xmlrpc]]></category>
		<category><![CDATA[xss]]></category>

		<guid isPermaLink="false">http://trainofthought.segfault.gr/?p=1930</guid>
		<description><![CDATA[Yes, yes&#8230;the time has finally come and there are boons for everyone. This release features the most impressive ChangeLog yet and the first (and coolest) thing in this long list is the brand new High Performance Grid implementation &#8212; which has been discussed extensively in the past. Let&#8217;s review the big points. New RPC infrastructure [...]]]></description>
				<content:encoded><![CDATA[<p>Yes, yes&#8230;the time has finally come and there are boons for everyone.<br />
This release features the most impressive <a href="http://arachni.segfault.gr/latest#v0.4">ChangeLog</a> yet and the first (and coolest) thing in this long list is the brand new <a href="https://github.com/Arachni/arachni/wiki/RPC-server#wiki-grid">High Performance Grid implementation</a> &#8212; which has been <a href="http://trainofthought.segfault.gr/?s=grid">discussed extensively</a> in the past.</p>
<p>Let&#8217;s review the big points.</p>
<h2><a href="https://github.com/Arachni/arachni-rpc">New RPC infrastructure</a></h2>
<p>Ruby&#8217;s XMLRPC has been ditched (as initially discussed in these two [<a href="http://trainofthought.segfault.gr/2011/09/11/ruby-rpc-hurdles/">1</a>, <a href="http://trainofthought.segfault.gr/2011/09/29/grid-sitrep-and-the-new-arachni-rpc-library/">2</a>] posts) in favor of <a href="https://github.com/Arachni/arachni-rpc">Arachni-RPC</a>.<br />
Arachni-RPC is lightweight, simple and fast which makes it ideal for large Grid deployments and makes it easy for 3rd parties to interoperate with Arachni&#8217;s servers.</p>
<p><strong>Notice:</strong> If you were using the old XMLRPC interface please update your code to use the new <a href="https://github.com/Arachni/arachni/wiki/RPC-API">RPC API</a>.</p>
<h2><a href="https://github.com/Arachni/arachni/wiki/RPC-server#wiki-grid">High Performance Grid</a></h2>
<p>I&#8217;ve been talking about this one so much that I&#8217;ve actually grown a bit sick of it &#8212; joking aside though this is one of Arachni&#8217;s most important features.<br />
It allows you to connect multiple nodes into a Grid and use them to perform lightning-fast scans.</p>
<p>This is due to the way Arachni distributes the workload, which is finely grained down to individual page elements to ensure fair and optimal distribution; because workload distribution is so fluid it effectively becomes a sort of bandwidth and CPU aggregation.</p>
<p>To put this in simple(-istic) terms:<br />
If you have 2 Amazon instances and you need to scan one site, by utilising the HPG you&#8217;ll be able to cut the scan time down to approximately half of what it would take by using a single node (plus the initial crawl time).</p>
<p>And if you have a huge site you can use 50 nodes and so the story goes&#8230;</p>
<p>This feature was an imaginary, almost unattainable, milestone back when I added the initial client/server implementation and I didn&#8217;t really think that I&#8217;d ever be able to make it happen.<br />
Luckily, I was wrong and I&#8217;m proud to present you with the first Open Source High Performance Grid web application security scanner!<br />
(By the way, does anyone know of a commercial scanner that can do this?)</p>
<p><strong>Notice:</strong> With the WebUI&#8217;s updated AutoDeploy add-on you&#8217;ll be able to go into World domination mode by performing point and click Grid deployments!<br />
<strong>Another notice:</strong> Use responsibly, don&#8217;t DDoS people.<br />
<strong>Yet another notice:</strong> It&#8217;s still considered experimental so <a href="https://github.com/Arachni/arachni/issues">let me know</a> if you come across a bug. <img src='http://trainofthought.segfault.gr/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<h2><a href="https://github.com/Arachni/arachni/wiki/Web-user-interface">Updated WebUI</a></h2>
<p>The WebUI now contains a few context-sensitive help dialogs to help out the newcomers and it has been updated to use the Thin webserver to send responses asynchronously in order to increase performance and feel &#8220;snappier&#8221;.<br />
It also supports HTTP basic auth just in case you want some simple password protection <strong>and</strong> has been updated to provide access to the brand new HPG goodies.</p>
<h2>Spider improvements</h2>
<p>There was a bug with redirections that prevented the spider from achieving optimal coverage which has now been resolved.<br />
More than that, the scope of the crawl can now be either extended or restricted by supplying newline-separated lists of URLs which should help you import 3rd party sitemaps.</p>
<h2>Plugins</h2>
<p>The plugin API has been extended in order to allow plugins to let the framework know if they can be distributed across HPG Instances and, if so, how to merge their results for the final report.</p>
<p>Another big (although invisible to the end-user) change is the conversion of all meta-modules to full-fledged plugins to simplify management and Grid distribution.</p>
<p>And these new plugins have been added:</p>
<ul>
<li>    ReScan &#8212; It uses the AFR report of a previous scan to extract the sitemap in order to avoid a redundant crawl.</li>
<li>    BeepNotify &#8212; Beeps when the scan finishes.</li>
<li>    LibNotify &#8212; Uses the libnotify library to send notifications for each discovered issue and a summary at the end of the scan.</li>
<li>    EmailNotify &#8212; Sends a notification (and optionally a report) over SMTP at the end of the scan.</li>
<li>    Manual verification &#8212; Flags issues that require manual verification as untrusted in order to reduce the signal-to-noise ratio.</li>
<li>    Resolver &#8212; Resolves vulnerable hostnames to IP addresses.</li>
</ul>
<h2>Modules</h2>
<p>I&#8217;ve got both good and bad news for this&#8230;.<br />
In an attempt to cleanup and optimise pattern matching in v0.3 I inadvertently broke some aspects of it which crippled the XSS (xss), SQL injection (sqli) and Path Traversal (path_traversal) modules &#8211;I sincerely apologise, mea culpa.</p>
<p>The good news is that I&#8217;ve made things right, cleaned up the API and the existing modules and improved their accuracy.</p>
<h2>Reports</h2>
<p>The HTML report has waved goodbye to Highcharts due to licensing reasons and now uses <a href="http://www.jqplot.com/">jqPlot</a> for all its charting and graphing needs.<br />
I&#8217;ve also removed the &#8220;report false-positive&#8221; button since a part of that process required RSA encryption which for some reason caused segfaults on Mac OSX.<br />
Good news is that the HTML reports will be <strong>significantly</strong> smaller in size from now on.</p>
<p>Moreover, the following new report formats have been added:</p>
<ul>
<li>    JSON &#8212; Exports the audit results as a JSON serialized Hash.</li>
<li>    Marshal &#8212; Exports the audit results as a Marshal serialized Hash.</li>
<li>    YAML &#8212; Exports the audit results as a YAML serialized Hash.</li>
</ul>
<h2>Cygwin package for Windows</h2>
<p>About time indeed, Windows users can now enjoy Arachni&#8217;s features &#8212; albeit via a preconfigured Cygwin environment.<br />
The important point is that you no longer have to manually hassle to install Arachni via MinGW or Cygwin yourselves or use a VM and what have you&#8230;<br />
Simply download and run the self-extracting archive, double click the &#8220;Cygwin&#8221; batch file and lo and behold: you&#8217;ve got a bash shell ready to execute Arachni&#8217;s scripts.</p>
<p>Unfortunately, there&#8217;s a performance penalty involved when running Arachni in Cygwin but until I port it to run natively on Windows it&#8217;ll have to do.</p>
<p>Before I forget, the <a href="https://github.com/Arachni/arachni/wiki">Wiki</a> has been cleaned up and brought up to date so if you need to go through the documentation that should be your first stop.</p>
<p>Go ahead and enjoy this new release everyone, download links and instructions here: <a href="http://arachni.segfault.gr/latest">http://arachni.segfault.gr/latest</a>.</p>
<p>Cheers,<br />
Tasos L.</p>
]]></content:encoded>
			<wfw:commentRss>http://trainofthought.segfault.gr/2012/01/07/arachni-v0-4-is-out/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Playing around with DOM XSS (Demo included)</title>
		<link>http://trainofthought.segfault.gr/2011/12/12/playing-around-with-dom-xss-demo-included/</link>
		<comments>http://trainofthought.segfault.gr/2011/12/12/playing-around-with-dom-xss-demo-included/#comments</comments>
		<pubDate>Mon, 12 Dec 2011 00:21:42 +0000</pubDate>
		<dc:creator>Zapotek</dc:creator>
				<category><![CDATA[Open Source]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Web Application]]></category>
		<category><![CDATA[cross site scripting]]></category>
		<category><![CDATA[detection]]></category>
		<category><![CDATA[dom]]></category>
		<category><![CDATA[experiment]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[therubyracer]]></category>
		<category><![CDATA[vulnerability]]></category>
		<category><![CDATA[xss]]></category>

		<guid isPermaLink="false">http://trainofthought.segfault.gr/?p=1918</guid>
		<description><![CDATA[I&#8217;ve caught a bug it seems and because I can&#8217;t just sit on my ass all day I figured why not play around with my latest toy. I&#8217;ve updated the code to make the process more streamlined and allow for fuzzing (or at least altering) some possible input vectors. Things will be very very simple [...]]]></description>
				<content:encoded><![CDATA[<p>I&#8217;ve caught a bug it seems and because I can&#8217;t just sit on my ass all day I figured why not play around with <a href="https://github.com/Zapotek/dom-js-experiment">my latest toy</a>.</p>
<p>I&#8217;ve updated the code to make the process more streamlined and allow for fuzzing (or at least altering) some possible input vectors.<br />
Things will be very very simple for now as I&#8217;m merely trying to demo that with appropriate effort invested this can become a viable solution &#8212; eventually.</p>
<p>To the point, I&#8217;ll showcase a DOM XSS vulnerability that will take place purely on the client-side.<br />
Unfortunately, there are a lot of DOM interfaces/vectors that I haven&#8217;t yet implemented so let&#8217;s stick to one I have &#8212; <em>navigator.userAgent</em>.</p>
<p></p><pre class="crayon-plain-tag">require_relative 'init'

html = <<EOHTML
<html>
    <head>
        <title>My title!</title>
    </head>

    <body>
        <div>
            <script type="text/javascript">
                document.write( navigator.userAgent );
            </script>
        </div>
    </body>
</html>
EOHTML

#
# the second param sets 'dont_eval_js' to true
#
# we want to do that ourselves later on after we've prepared the vectors
# (navigator.userAgent in this case)
#
window = DOM::Window.new( html, true )

#
# we'll inject this fictional tag and look for it in the DOM structure later on
#
# if found, then we have an XSS vuln
#
seed_tag = 'myinjectedtag'

# our XSS vector
window.navigator.userAgent = "<#{seed_tag}>blah blah blah</#{seed_tag}"

# this will show the HTML as is (Ref. #1)
# puts window.document.to_html
# puts '-' * 80

# execute the JS
window.instance_eval { exec_js! }

# this one will show the updated HTML i.e. including our tag  (Ref. #2)
# puts window.document.to_html

# look for the tag in the DOM structure
if window.document.getElementsByTagName( seed_tag )[0]
    puts 'Vulnerable to XSS!'
end</pre><p></p>
<p><strong>Ref. #1</strong><br />
This is what the code looks like at this point:<br />
<pre class="crayon-plain-tag"><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title>My title!</title>
</head>
<body>
        <div>
            <script type="text/javascript">
                document.write( navigator.userAgent );
            </script>
</div>
    </body>
</html></pre></p>
<p>Pretty much what we passed&#8230;</p>
<p><strong>Ref. #2</strong><br />
And now that the JS has been executed:<br />
<pre class="crayon-plain-tag"><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title>My title!</title>
</head>
<body>
        <div>
            <script type="text/javascript">
                document.write( navigator.userAgent );
            </script>
</div>
    </body>
<myinjectedtag>blah blah blah</myinjectedtag>
</html></pre></p>
<p>Yes I know, <em>document.write()</em> should have written inside the parent of the <em>script</em> tag, the <em>div</em> in this case.<br />
I can&#8217;t be bothered to take care of this right now, this is just a prototype&#8230;a prototype of a prototype of the prototype actually.</p>
<p><strong>End result</strong><br />
Since we can see that the injection was clearly successful, the inevitable message shall appear:<br />
<pre class="crayon-plain-tag">Vulnerable to XSS!</pre></p>
<p><strong>Conclusion</strong><br />
Yeah it&#8217;s really basic and simplistic and not a big deal but it&#8217;s fun to see things working &#8212; barely but still, heh.</p>
]]></content:encoded>
			<wfw:commentRss>http://trainofthought.segfault.gr/2011/12/12/playing-around-with-dom-xss-demo-included/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Experimentng with DOM and JavaScript support for Ruby</title>
		<link>http://trainofthought.segfault.gr/2011/12/05/experimentng-with-dom-and-javascript-support-for-ruby/</link>
		<comments>http://trainofthought.segfault.gr/2011/12/05/experimentng-with-dom-and-javascript-support-for-ruby/#comments</comments>
		<pubDate>Mon, 05 Dec 2011 01:33:34 +0000</pubDate>
		<dc:creator>Zapotek</dc:creator>
				<category><![CDATA[Arachni]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Projects]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Web Application]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[arachni]]></category>
		<category><![CDATA[dom]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[nokogiri]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[scanner]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[taka]]></category>
		<category><![CDATA[therubyracer]]></category>
		<category><![CDATA[v8]]></category>
		<category><![CDATA[vulnerability]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://trainofthought.segfault.gr/?p=1898</guid>
		<description><![CDATA[One of the things everyone is taking for granted nowadays for every browser and website is decent support for AJAX. Naturally, scanner devs have been trying to find a decent way to automatically audit that side of the fence or at least provide decent coverage for JS-heavy webapps. Thing is though&#8230; this is a bitch [...]]]></description>
				<content:encoded><![CDATA[<p>One of the things everyone is taking for granted nowadays for every browser and website is decent support for AJAX.<br />
Naturally, scanner devs have been trying to find a decent way to automatically audit that side of the fence or at least provide decent coverage for JS-heavy webapps.</p>
<p>Thing is though&#8230; this is a <strong>bitch</strong> to get right.<br />
And never mind getting it <i>right</i>, it&#8217;s hard enough getting the damn thing to work to begin with.</p>
<p>There are 3 things that need to be integrated in order to achieve that sort of functionality:</p>
<ol>
<li>a <a href="http://en.wikipedia.org/wiki/Document_Object_Model">DOM</a></li>
<li>a <a href="http://en.wikipedia.org/wiki/List_of_ECMAScript_engines">JS interpreter</a></li>
<li>a <a href="http://en.wikipedia.org/wiki/Ajax_%28programming%29">AJAX extension</a> to JS</li>
</ol>
<h2>DOM</h2>
<p>The static parts of the DOM can easily be built on top of tested and proven XML parsers like libxml.<br />
The DOM has tricky parts too though, which are where the money is, like timers and events.</p>
<p>It&#8217;s alright though, a little bit of smart thread scheduling and clean collections of callbacks will sustain you at the beginning.</p>
<h2>JS</h2>
<p>JS integration difficulty depends on your language of choice.<br />
Thankfully, more and more bindings for several different JS engines are being released so you can have your pick.<br />
The interfaces can be a bit dodgy though at times which can defeat the whole purpose.</p>
<h2>AJAX</h2>
<p>AJAX functionality will have to fall on your shoulders but if you managed to get the first 2 parts working this won&#8217;t be much of a challenge.<br />
You simply write an AJAX API in your language of choice and make that interface available to the JS code.</p>
<h2>Where I&#8217;m going with this&#8230;</h2>
<p>It&#8217;s certainly possible to get these working together (without being a multi-million dollar corporation even) but it&#8217;ll be <strong>a lot</strong> of work.<br />
Which is the reason there&#8217;s no open source scanner that supports AJAX or even basic JS scripting.</p>
<p>Arachni is no exception, not to mention that due to the young age of the system there had been far more important and basic things to be worked out first.</p>
<p>Luckily, a lot of things have changed in very little time.<br />
The project still has a few bugs but it has been stable enough for a few businesses to build some of their infrastructure on it.<br />
And the v0.4 version if pretty much ready, which takes care of another big feature I could not wait to implement &#8212; the High Performance Grid.</p>
<p>Next stop: AJAX</p>
<p>Some time ago I was contacted by a CompSci student who had chosen to add AJAX support to Arachni as his final-year project, nice guy and seems motivated so I&#8217;m pretty sure that this is gonna happen.</p>
<p>And since he mentioned it I decided to start looking into the subject matter myself with this:<br />
<a href="https://github.com/Zapotek/dom-js-experiment">https://github.com/Zapotek/dom-js-experiment</a></p>
<p>It&#8217;s based on <a href="https://github.com/tenderlove/taka">Taka</a> (for the static parts of the DOM) and the <a href="http://code.google.com/p/v8/">V8 JS engine</a> as provided by <a href="https://github.com/cowboyd/therubyracer">TheRubyRacer</a>.</p>
<p>I&#8217;ve managed to make this work with some simple stuff, JQuery loads without errors and kind of works (I haven&#8217;t had much time to test it).<br />
I haven&#8217;t had time to implement timers and events yet but they&#8217;re coming&#8230;<br />
I don&#8217;t want to finish this thing on my own though, the other guy will need to work on it for his project.</p>
<p>Point is&#8230;AJAX support is coming to Arachni; it will of course take time but it&#8217;s going to happen.</p>
]]></content:encoded>
			<wfw:commentRss>http://trainofthought.segfault.gr/2011/12/05/experimentng-with-dom-and-javascript-support-for-ruby/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Programatically scanning using Arachni (Part 5)</title>
		<link>http://trainofthought.segfault.gr/2011/12/04/programatically-scanning-using-arachni-part-5/</link>
		<comments>http://trainofthought.segfault.gr/2011/12/04/programatically-scanning-using-arachni-part-5/#comments</comments>
		<pubDate>Sun, 04 Dec 2011 03:03:41 +0000</pubDate>
		<dc:creator>Zapotek</dc:creator>
				<category><![CDATA[Arachni]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Projects]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Web Application]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[arachni]]></category>
		<category><![CDATA[audit]]></category>
		<category><![CDATA[automated]]></category>
		<category><![CDATA[crawler]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[differential analysis]]></category>
		<category><![CDATA[framework]]></category>
		<category><![CDATA[module]]></category>
		<category><![CDATA[modules]]></category>
		<category><![CDATA[open source]]></category>
		<category><![CDATA[pattern matching]]></category>
		<category><![CDATA[plugins]]></category>
		<category><![CDATA[rdiff]]></category>
		<category><![CDATA[report]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[scanner]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[spider]]></category>
		<category><![CDATA[timing attacck]]></category>
		<category><![CDATA[vulnerability]]></category>
		<category><![CDATA[vulnerability scanner]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[web application]]></category>
		<category><![CDATA[webapp]]></category>

		<guid isPermaLink="false">http://trainofthought.segfault.gr/?p=1839</guid>
		<description><![CDATA[As promised, part 5. Not that anyone&#8217;s reading this crap, once I&#8217;m done with the series though I&#8217;ll be able to gather them into a nice developer&#8217;s guide so I might as well keep going. As always, keep your installation to up date with the experimental branch before continuing. These articles have forced me to [...]]]></description>
				<content:encoded><![CDATA[<p>As promised, part 5.<br />
Not that anyone&#8217;s reading this crap, once I&#8217;m done with the series though I&#8217;ll be able to gather them into a nice developer&#8217;s guide so I might as well keep going.</p>
<p>As always, keep your installation to up date with the experimental branch before continuing.<br />
These articles have forced me to see Arachni from a completely different perspective and so I keep improving the API to make it more developer friendly.</p>
<p>This time we&#8217;ll focus on auditing individual elements and also work on a per page scope.</p>
<p>Let me paint you a picture:</p>
<blockquote><p>You have a Rails (or some other such framework) web application.<br />
You need to audit it in a consistent manner.<br />
You wish you could simply add security tests to your existing test suite next to your units/functional/integration/etc. tests.<br />
Your webapp framework already keeps track of pretty much all inputs (and if not you can override helper methods like <i>link_to</i> and <i>form_for</i> to keep track of them).<br />
The only thing missing is a system to which you can feed that data and audit those inputs.</p></blockquote>
<p>You see where I&#8217;m going with this, right?</p>
<p>The Arachni framework can easily handle this in a number of ways, some of which I&#8217;ll demonstrate here.<br />
<span id="more-1839"></span></p>
<h2>Scripted element audits</h2>
<p>Let&#8217;s start from the outside and move inwards.<br />
The framework feeds pages to modules &#8211;> modules audit pages &#8211;> pages include elements &#8211;> modules inherit from <i>Arachni::Module::Base</i> &#8211;> <i>Arachni::Module::Base</i> includes <i>Arachni::Module::Auditor</i> and <i>Arachni::Module::Output</i>.</p>
<p>And everything&#8217;s at its place, modules live under &#8220;/modules&#8221; and everyone&#8217;s happy.</p>
<p>I&#8217;ve already showed how to script an audit but what if you want a narrower scope?<br />
What if you have a list of elements you want to audit?<br />
What if you want to pass a custom page to be audited?<br />
And more importantly, how can you do that without needing to work with the framework, the module manager and without needing to write a full blown module?<br />
What if you just want the functionality without the hassle?</p>
<p>Obviously that&#8217;s possible, otherwise I wouldn&#8217;t have gone through that sales pitch.<br />
What we need to do is create our own Auditor at runtime&#8230; better yet, let&#8217;s create our own Module so that way we&#8217;ll have access to some utilities and other toys as well.<br />
Through that Auditor we&#8217;ll be able to submit, fuzz, audit, analyze elements and server responses.</p>
<p>Time to go through the 2 cleanest ways to do that.</p>
<p>First of all, this will be our Auditor, it&#8217;ll serve as a proxy to the functionality enjoyed by regular framework modules.<br />
<strong>auditor.rb</strong></p><pre class="crayon-plain-tag">require 'arachni/ui/cli/output'
require 'arachni'

#
# This class is capable of providing module-like functionality to 3rd party objects.
#
# This is the same as a regular module but instead of including callbacks like:
#    * prepare
#    * run
#    * clean_up
#
# to perform its job, its functionality is exploited by other classes.
#
class Auditor < Arachni::Module::Base

    #
    # If the parameter is a Arachni::Parser::Page that's going to be the
    # reference page of the auditor.
    #
    # If the param is a string it'll be treated like a URL and a new page
    # will be created for it.
    #
    def initialize( page_or_url )
        # setup a minimal page to have as a reference
        if !page_or_url.is_a? Arachni::Parser::Page
            super Arachni::Parser::Page.new( :url => page_or_url.to_s )
        else
            super page_or_url
        end
    end

    def self.info; { :name => 'My auditor' } end
end</pre><p></p>
<p>Each Auditor <strong>must</strong> have a page as a reference if only to know the current URL, so a page with at least a URL assigned must be available at all times.</p>
<h3>Per element</h3>
<p>This approach deals with auditing individual, user created, elements, which is the finest grained of controls possible.<br />
Talk is cheap though, let&#8217;s test a few examples.</p>
<h4>Pattern matching</h4>
<p>Simple enough, inject a string and see if it appears in the response:</p><pre class="crayon-plain-tag">require_relative 'auditor'

opts = Arachni::Options.instance

opts.audit_links = true
opts.url         = 'http://testfire.net'

# the owner is supposed to be the URL of the page containing the element
owner = opts.url.to_s

# let's create a link element to audit
link = Arachni::Parser::Element::Link.new( owner, {

    # action, to which URL to send the data
    :action => owner + '/notfound.aspx',

    # auditable inputs, these will be fuzzed/audited/whatever
    :inputs  => {
        'aspxerrorpath' => ''
    }
})

# seed to inject
injection_str = '<xss />'

# some audit options
audit_opts = {
    # the existence of this substring in the response will verify the issue
    # :substring => injection_str,

    # is this is a string it'll be converted to a regexp
    :regexp    => injection_str,

    # controls the format of the injected string,
    # this one means: inject as is
    :format    => [ Arachni::Module::Auditor::Format::STRAIGHT ]
}

# get an auditor and pass it the seed URL, it'll also be used as the page URL
auditor = Auditor.new( opts.url.to_s )

# the name is a dead give-away
#
# the 1st param is an array of elements to audit
# the 2nd one is the string to inject
# the 3rd one is a hash with audit options
#
# if a server response contains the :substring (or matches a :regexp)
# then an issue will be logged automatically
#
auditor.audit_elems( [link], injection_str, audit_opts )

# this fires the queued requests and blocks until all responses have arrived
auditor.http.run

# grab any logged issues/results
results = Arachni::Module::Manager.results
auditor.print_info "Found " + results.count.to_s + " matches."</pre><p></p>
<p>This time we don&#8217;t mute the output, and why should we? Let&#8217;s admire our handy work. <img src='http://trainofthought.segfault.gr/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>But what if you don&#8217;t want the auditor to log the the issue automatically based on an expression or substring?</p><pre class="crayon-plain-tag">audit_opts = {
    :format    => [ Arachni::Module::Auditor::Format::STRAIGHT ]
}

# if you pass a block you get full control over how to analyze the audit responses
auditor.audit_elems( [link], injection_str, audit_opts ) {
    |response, options, element|
    # full HTTP response
    # pp response

    # expanded options
    # ap options

    # updated element
    # pp element

    # perform your own analysis and logging
    auditor.log( options, response ) if response.body.include?( injection_str )
}</pre><p></p>
<h4>Differential analysis</h4>
<p>The Auditor module contains helpers that perform differential analysis for you using Arachni&#8217;s own <a href="http://trainofthought.segfault.gr/?s=rdiff">rDiff algorithm</a>.</p>
<p>See this SQL injection example for instance:</p><pre class="crayon-plain-tag">require_relative 'auditor'

opts = Arachni::Options.instance

opts.audit_links = true
opts.url         = 'http://testphp.vulnweb.com/artists.php'

# the owner is supposed to be the URL of the page containing the element
owner = opts.url.to_s

# let's create a link element to audit
link = Arachni::Parser::Element::Link.new( owner, {

    # action, to which URL to send the data
    :action => owner,

    # auditable inputs, these will be fuzzed/audited/whatever
    :inputs  => {
        'artist' => '1'
    }
})

# get an auditor and pass it the seed URL, it'll also be used as the page URL
auditor = Auditor.new( opts.url.to_s )

rdiff_opts = {
    #
    # fault injection seeds
    #
    # these must generate an exceptional condition or
    # general error when appended to an SQL query
    #
    :faults => [ '\'"<pre class="inline:true decode:1 " >' ],

    #
    # boolean injection seeds
    #
    # these values should not affect the behavior of the page when evaluated
    # as part of the SQL query
    #
    :bools => [ ' and 1+1 = 2' ],

    #
    # rDiff precision
    #
    # Specifies the amount of rdiff iterations,
    # more iterations => more refined/accurate result
    #
    # default is 2 but what the hell...
    #
    :precision => 5
}

# analyze and log
auditor.audit_rdiff_elem( link, rdiff_opts )

# this fires the queued requests and blocks until all responses have arrived
auditor.http.run

# grab any logged issues/results
results = Arachni::Module::Manager.results
auditor.print_info "Found " + results.count.to_s + " matches."</pre><p></p>
<p>Again, if you want to perform your own analysis and logging:</p><pre class="crayon-plain-tag">auditor.audit_rdiff_elem( link, rdiff_opts ) {
    |injection_string, element, original_response_body, bool_response, fault_response_body|

    # injected string
    # p injection_string

    # updated audited element element
    # pp element

    # body of the original/vanilla page
    # pp original_response_body

    # full HTTP response for the boolean injection
    # pp bool_response

    # body of the fault injection response
    # pp fault_response_body
}</pre><p></p>
<h4>Time-out/delay attacks</h4>
<p>Yes, timing attacks are of course part of the repertoire but are not available on a per-element basis.<br />
Don&#8217;t worry though, there&#8217;s an alternative way to perform them as we&#8217;ll see shortly.</p>
<h3>Per page</h3>
<p>The per-page approach is slightly different and requires some commitment as you have to you properly configure a page object with elements and feed that page object to the module.<br />
The module then audits those elements in batch.</p>
<h4>Pattern matching</h4>
<p>Let&#8217;s see what the pattern matching example looks like when adjusted for this approach:</p><pre class="crayon-plain-tag">require_relative 'auditor'

opts = Arachni::Options.instance

opts.audit_links = true
opts.url         = 'http://testfire.net'

owner = opts.url.to_s

link = Arachni::Parser::Element::Link.new( owner, {
    :action => owner + '/notfound.aspx',
    :inputs => {
        'aspxerrorpath' => ''
    }
})

# create our page and assign it the link
page = Arachni::Parser::Page.new(
    :url   => owner.to_s,
    :links => [link]
)

# get an auditor and pass it the page
auditor = Auditor.new( page )

injection_str = '<xss />'
audit_opts = {
    :regexp    => injection_str,
    :format    => [ Arachni::Module::Auditor::Format::STRAIGHT ]
}

auditor.audit( injection_str, audit_opts )

auditor.http.run

results = Arachni::Module::Manager.results
auditor.print_info "Found " + results.count.to_s + " matches."</pre><p>I&#8217;ve removed most of the comments this time since they were identical to the first example.</p>
<p>I just had an idea, let&#8217;s add an <i>audit</i> method to the page object itself and see if it&#8217;s any easier to work with.</p><pre class="crayon-plain-tag">require_relative 'auditor'

opts = Arachni::Options.instance

opts.audit_links = true
opts.url         = 'http://testfire.net'

owner = opts.url.to_s

class Arachni::Parser::Page
    def audit!( injection_str, opts, run = true, &block )
        auditor = Auditor.new( self )
        auditor.audit( injection_str, opts, &block )
        auditor.http.run if run
    end
end

link = Arachni::Parser::Element::Link.new( owner, {
    :action => owner + '/notfound.aspx',
    :inputs => {
        'aspxerrorpath' => ''
    }
})

# create our page and assign it the link
page = Arachni::Parser::Page.new(
    :url   => owner.to_s,
    :links => [link]
)

injection_str = '<xss />'
audit_opts = {
    :regexp    => injection_str,
    :format    => [ Arachni::Module::Auditor::Format::STRAIGHT ]
}

page.audit!( injection_str, audit_opts )

results = Arachni::Module::Manager.results
puts "Found " + results.count.to_s + " matches."</pre><p></p>
<p>Huh, seems kinda nice&#8230; <img src='http://trainofthought.segfault.gr/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<h4>Differential analysis</h4>
<p>Should be a familiar sight:</p><pre class="crayon-plain-tag">require_relative 'auditor'

opts = Arachni::Options.instance

opts.audit_links = true
opts.url         = 'http://testphp.vulnweb.com/artists.php'

owner = opts.url.to_s

link = Arachni::Parser::Element::Link.new( owner, {
    :action => owner,
    :inputs  => {
        'artist' => '1',
    }
})

page = Arachni::Parser::Page.new( :url => owner )
page.links << link

auditor = Auditor.new( page )

rdiff_opts = {
    :faults => [ '\'"</pre><p>&#8216; ],<br />
    :bools => [ ' and 1+1 = 2' ],<br />
}</p>
<p>auditor.audit_rdiff( rdiff_opts )<br />
auditor.http.run</p>
<p>results = Arachni::Module::Manager.results<br />
auditor.print_info &#8220;Found &#8221; + results.count.to_s + &#8221; matches.&#8221;
</pre>
<h4>Time-out/delay attacks</h4>
<p>And now it's time to check out some timing attacks.<br />
Actually, since timing attacks are wildly unreliable Arachni doesn't simply perform a time-out check.<br />
It performs a few time-out checks with different delay tolerances and waits for the server to normalize in between and it also does some other weird stuff to make sure that false positives are a rare occasion.</p>
<p></p><pre class="crayon-plain-tag">require_relative 'auditor'

opts = Arachni::Options.instance

opts.audit_links = true
opts.url         = 'http://testphp.vulnweb.com/artists.php'

owner = opts.url.to_s

link = Arachni::Parser::Element::Link.new( owner, {
    :action => owner,
    :inputs  => {
        'artist' => '1',
    }
})

page = Arachni::Parser::Page.new( :url => owner )
page.links << link

auditor = Auditor.new( page )

timeout_opts = {
    :format  => [ Arachni::Module::Auditor::Format::STRAIGHT ],

    #
    # set first stage timeout to 4 seconds
    # the rest of the stages are based on this value
    #
    :timeout => 4000,

    #
    # different platforms expect different time representations,
    # some expect seconds and others milliseconds.
    #
    # __TIME__ in the injection string will be replaced with:
    #    :timeout / :timeout_divider
    #
    #
    # optional and defaults to 1
    #
    :timeout_divider => 1000
}

auditor.audit_timeout( [ 'sleep(__TIME__)#' ], timeout_opts )

# timing attacks are a bit different and are scheduled to be run at the end
# of a full audit
#
# that's why simply firing up the queued HTTP requests (like with the other methods)
# isn't enough
Arachni::Module::Auditor.timeout_audit_run

results = Arachni::Module::Manager.results
auditor.print_info "Found " + results.count.to_s + " matches."</pre><p></p>
<p>Simple as pie.</p>
<h3>Page delegation</h3>
<p>Let's investigate this idea a bit further, shall we?<br />
Having audit functionality built-into the page objects would surely be helpful!</p>
<p>Time to monkey-patch the Page class I reckon so update your <strong>auditor.rb</strong> file to this:</p><pre class="crayon-plain-tag">require 'arachni/ui/cli/output'
require 'arachni'

#
# This class is capable of providing module-like functionality to 3rd party objects.
#
# This is the same as a regular module but instead of including callbacks like:
#    * prepare
#    * run
#    * clean_up
#
# to perform its job, its functionality is exploited by other classes.
#
class Auditor < Arachni::Module::Base

    #
    # If the parameter is a Arachni::Parser::Page that's going to be the
    # reference page of the auditor.
    #
    # If the param is a string it'll be treated like a URL and a new page
    # will be created for it.
    #
    def initialize( page_or_url )
        # setup a minimal page to have as a reference
        if !page_or_url.is_a? Arachni::Parser::Page
            super Arachni::Parser::Page.new( :url => page_or_url.to_s )
        else
            super page_or_url
        end
    end

    def self.info; { :name => 'My auditor' } end
end

class Arachni::Parser::Page

    #
    # Trap any called methods that don't exist in the page class
    # and send them to the Auditor class if they are audit methods.
    #
    def method_missing( sym, *args, &block )
        tokens = sym.to_s.split( '_' )

        if tokens.first != 'audit'
            raise NoMethodError.new( "Undefined method '#{sym.to_s}'.", sym, args )
        end

        @auditor ||= ::Auditor.new( self )
        @auditor.method( sym ).call( *args, &block )

        # check for a :run flag explicitly since we may need to queue up
        # a number of audits
        opts = {}
        args.flatten.each {
            |arg|
            opts.merge!( arg ) if arg.is_a?( Hash )
        }
        return if !opts[:run]

        if tokens.last != 'timeout'
            @auditor.http.run
        else
            ::Arachni::Module::Auditor.timeout_audit_run
        end

    end
end</pre><p></p>
<p>And from that point on you can simply audit pages like so:</p>
<h4>Audit combo</h4>
<p></p><pre class="crayon-plain-tag">require_relative 'auditor'

opts = Arachni::Options.instance

opts.audit_links = true
opts.audit_forms = true
opts.url         = 'http://testphp.vulnweb.com/'

owner = opts.url.to_s

page = Arachni::Parser::Page.new( :url => owner )

#
# Vulnerable to SQL injection and will be flagged by rDiff and timeout attacks.
#
page.links << Arachni::Parser::Element::Link.new( owner, {
    :action => owner + 'artists.php',
    :inputs  => {
        'artist' => '1',
    }
})

#
# Vulnerable to XSS.
#
page.forms << Arachni::Parser::Element::Form.new( owner, {
    :action  => owner + 'search.php',
    :method  => :post,
    :inputs  => {
        'searchFor' => ''
    }
})

#
# rDiff audit
#
# Just queue the requests, don't run them yet.
# It's better if we combine these requests with the pattern matching audit
#
rdiff_opts = {
    :faults => [ '\'"`' ],
    :bools => [ ' and 1+1 = 2' ]
}
page.audit_rdiff( rdiff_opts )


#
# Pattern matching
#
# Will submit the queued HTTP requests.
#
injection_str = '<xss />'
audit_opts = {
    :regexp    => injection_str,
    :format    => [ Arachni::Module::Auditor::Format::STRAIGHT ],
    :run => true
}
page.audit( injection_str, audit_opts )


#
# Timing attack
#
# It's better to run it at the end of the audit on its own .
#
timeout_opts = {
    :format  => [ Arachni::Module::Auditor::Format::STRAIGHT ],
    :timeout => 4000,
    :timeout_divider => 1000,
    :run => true
}
page.audit_timeout( [ 'sleep(__TIME__)#' ], timeout_opts )


results = Arachni::Module::Manager.results
puts "Found " + results.count.to_s + " matches."</pre><p></p>
<h3>Running all modules</h3>
<p>We may have gone a little bit too deep it seems...<br />
You may not want to perform the audit yourself...<br />
You may want to specify a narrow scope, on a per-element basis, but use the existing modules to audit them.<br />
How do you do that?</p>
<p>First of all, you can't give Arachni's modules individual elements...you have to give them pages.<br />
That's alright though since as we've seen today creating pages containing elements of our choosing is no big deal.</p><pre class="crayon-plain-tag">require 'arachni/ui/cli/output'
require 'arachni'

opts = Arachni::Options.instance

opts.audit_links = true
opts.url         = 'http://testfire.net'

owner = opts.url.to_s

page = Arachni::Parser::Page.new( :url   => owner.to_s )
page.links << Arachni::Parser::Element::Link.new( owner, {
    :action => owner + '/notfound.aspx',
    :inputs => {
        'aspxerrorpath' => ''
    }
})

modules = Arachni::Module::Manager.new( opts )

# doesn't make sense to load recon modules in this case
modules.load( ['audit/*'] )
modules.run( page )

Arachni::HTTP.instance.run

results = Arachni::Module::Manager.results
puts "Found " + results.count.to_s + " matches."</pre><p></p>
<p>That's all for now, I hope you that found this post interesting -- I certainly did.</p>
<p>Cheers</p>
]]></content:encoded>
			<wfw:commentRss>http://trainofthought.segfault.gr/2011/12/04/programatically-scanning-using-arachni-part-5/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
	</channel>
</rss>

<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Minified using disk
Page Caching using disk: enhanced
Database Caching 5/9 queries in 0.003 seconds using disk
Object Caching 1057/1070 objects using disk

 Served from: trainofthought.segfault.gr @ 2013-05-25 08:04:12 by W3 Total Cache -->