Please enable Javascript to view the contents

安全扫描工具Arachni源码分析(一)

 ·  ☕ 6 分钟

Arachni是一个基于Ruby on Rails框架的Web安全漏洞扫描工具。

1. Ruby on Rails

Ruby on Rails ,缩写ROR,是一个Web框架,包括两部分内容: Ruby 语言和 Rails 框架。Ruby一直以来流行于日本,直到2004年,26 岁的丹麦人 David Heinemeier Hansson 提出了Web框架 - Rails。这才让全世界人们,开始了解Ruby和Rails的灵活与高效。

1.1 Ruby

日本人松本行弘( Matsumoto Yukihiro),在 1993 年开始着手 Ruby 语言的研发工作。1995 年 12 月 推出了 Ruby 的第一个版本 Ruby 0.95。

语言特点:

  • 纯的面向对象语言
  • 解释型脚本语言
  • 动态载入
  • 自动内存管理机制
  • 多精度整数
  • 迭代器和闭包
  • 开源项目

1.2 Rails

Rails 结合了PHP快速开发、Java程序规整的优点,是一个符合实际需要而且更高效的 Web 开发框架。

框架特点:

  • 全栈式的 MVC 框架。自带Model, View,Controller层工具
  • 使用约定,而不是XML配置
  • 支架系统。可以自动为数据表创建CRUD操作和前台视图
  • 开发效率高,代码量少

2. Arachni-ui-web目录结构

Arachni-ui-web,是Arachni的一个前端交互。Arachni 核心部分在,system/ruby/lib/ruby/gems/2.2.0/gems/arachni-1.5.1目录下。

2.1 bin目录

保存的是面向用户的执行脚本,提供基本的启动、操作命令。

2.2 system目录

包括Ruby执行环境,依赖包,项目的源码,日志目录,用户home目录等。可以说,system包含了项目所需的一切。

system/arachni-ui-web是项目的源码目录。其中:

  • app目录。项目的主目录,大部分的项目代码都在其中
  • app/assets目录。包含了前端的资源,javascript、css、images
  • app/controllers目录。包含了所有的controller,在Rails中一般controller就是指REST架构中的资源。controller里实现了各种action,用于响应web请求。 也是MVC架构中的C层
  • app/helper目录。用于存放一些helper方法,这些helper方法一般用在view层,用于组织一些用于view的逻辑代码
  • app/mailers目录。用于放和邮件发送相关的代码
  • app/models目录。用于放各种数据库映射的model,一些数据操作以及业务逻辑代码,都应该放在这里。 MVC架构中的M层
  • app/views目录。用于放views层的模板,经过controller渲染这些模板,最后生成可供用户访问的页面。MVC架构中的V层
  • bin目录。Rails的命令,以及bundle、rake命令
  • config目录。配置项目运行规则、数据库等
  • db目录。当前的数据库模式,以及数据库迁移
  • features目录。数据文件
  • lib目录。项目扩展包
  • log目录。系统日志文件
  • public目录。公共资源,包含静态文件和链接资源
  • script目录。 项目运行或清理的脚步
  • spec 目录。RSpec工具的测试文件
  • tmp目录。临时文件文件
  • vendor目录。第三方代码,插件等

其他重要文件:

  • config/routes.rb。
    指定项目所有的路由配置,也就是说,所有的web请求收发规则,都由这里指定
  • db/seeds.rb。
    初始化数据库

3. Arachni XSS

安全测试的代码,主要存放在 arachni-1.5.1/components/checks/

Arachni 检测XSS的原理是,随机生成一个ID或者指定一个特殊字符串,作为注入的XSS向量,如果页面内容中查找到了这个ID或字符串,则存在XSS漏洞。

函数的基本执行流程是,先拼接XSS向量(tag_name,strings,options),然后run( )执行,最后找证据(find_included_payload,check_and_log,find_proof)。

  • xss.rb。

直接注入<>标记的HTML,还可以注入带编码的XSS向量。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
    def self.tag_name
        "#{shortname}_#{random_seed}"
    end

    def self.tag
        "<#{tag_name}/>"
    end

    def self.strings
        @strings ||= [
            # Straight injection.
            tag,

            # Go for an error.
            "()\"&%1'-;#{tag}'",

            # Break out of HTML comments and text areas.
            "</textarea>-->#{tag}<!--<textarea>"
        ].map{ |p| [p, Form.encode( p ) ]}.flatten.uniq
    end
  • xss_dom.rb。

检测DOM型的XSS攻击。XSS DOM是,使用类似document.body.innerHTML的执行,动态向页面注入XSS。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
    prefer :xss

    def self.tag_name
        "#{shortname}_#{random_seed}"
    end

    def self.tag
        "<#{tag_name}/>"
    end

    def self.strings
        @strings ||= [
            # Straight injection.
            tag,

            # Break out of HTML comments and text areas.
            "</textarea>-->#{tag}<!--<textarea>"
        ]
    end
  • xss_dom_script_context.rb。

检测XSS DOM漏洞,注入的内容为JavaScript代码。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
    prefer :xss_script_context
    def self.seed
        'window.top._%s_taint_tracer.log_execution_flow_sink()'
    end

    def self.strings
        @strings ||= [
            "javascript:#{seed}//",
            "1;#{seed}//",
            "';#{seed}//",
            "\";#{seed}//",
            "*/;#{seed}/*"
        ]
    end
  • xss_event.rb。

基于事件和属性的XSS。类型有, ‘onload’, ‘onunload’, ‘onblur’, ‘onchange’, ‘onfocus’, ‘onreset’, ‘onselect’, ‘onsubmit’, ‘onabort’, ‘onkeydown’, ‘onkeypress’, ‘onkeyup’, ‘onclick’, ‘ondblclick’, ‘onmousedown’, ‘onmousemove’, ‘onmouseout’, ‘onmouseover’, ‘onmouseup’, ‘src’。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
    def self.attribute_name
        'arachni_xss_in_element_event'
    end

    def self.strings
        @strings ||= [
            ";#{attribute_name}=#{random_seed}//",
            "\";#{attribute_name}=#{random_seed}//",
            "';#{attribute_name}=#{random_seed}//"
        ].map { |s| [ " script:#{s}", " #{s}" ] }.flatten
    end
  • xss_path.rb。

拼接URL,检验反射型的XSS。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
    def self.tag
        @tag ||= 'my_tag_' + random_seed
    end

    def self.string
        @string ||= '<' + tag + '/>'
    end

    def self.requests
        @requests ||= [
            [ string, {} ],
            [ '>"\'>' + string, {} ],

            [ '', { string => '' } ],
            [ '', { '>"\'>' + string => '' } ],

            [ '', { '' => string } ],
            [ '', { '' => '>"\'>' + string } ]
        ]
    end
  • xss_script_context.rb。

利用 script标签注入JavaScript。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    def self.seed
        'window.top._%s_taint_tracer.log_execution_flow_sink()'
    end

    def self.strings
        return @strings if @strings

        @strings ||= [ "javascript:#{seed}" ]

        ['\'', '"', ''].each do |quote|
            [ "%q;#{seed}%q", "%q;#{seed};%q" ].each do |payload|
                @strings << payload.gsub( '%q', quote )
            end
        end

        [ "1;#{seed}%q", "1;\n#{seed}%q" ].each do |payload|
            ['', ';'].each do |s|
                @strings << payload.gsub( '%q', s )
            end
        end

        @strings = @strings.map { |s| [ s, "#{s}//" ] }.flatten
        @strings << "*/;\n#{seed}/*"

        # In case they're placed as assoc array values.
        @strings << seed
        @strings << "\",x:#{seed},y:\""
        @strings << "',x:#{seed},y:'"

        @strings << "</script><script>#{seed}</script>"
    end
  • xss_tag.rb。
    利用HTML标签属性执行XSS。
1
2
3
4
5
    ATTRIBUTE_NAME = 'arachni_xss_in_tag'
    def self.strings
        @strings ||= ['', '\'', '"'].
            map { |q| "#{q} #{ATTRIBUTE_NAME}=#{q}#{random_seed}#{q} blah=#{q}" }
    end
  • xxe.rb。

XML External Entity Injection,简称XXE,发生在应用程序解析 XML 输入时,没有禁止外部实体的加载。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
    def self.options
        @options ||= {
            format:        [Format::STRAIGHT],
            signatures:    FILE_SIGNATURES_PER_PLATFORM.select { |k, _| payloads.include? k },
            each_mutation: proc do |mutation|
                mutation.platforms.pick( payloads ).map do |platform, payloads|
                    payloads.map do |payload|
                        m = mutation.dup

                        m.transform_xml do |xml|
                            xml.sub( m.affected_input_value, "&#{ENTITY};" )
                        end

                        m.audit_options[:platform] = platform
                        m.source = "<!DOCTYPE #{ENTITY} [ <!ENTITY #{ENTITY} SYSTEM \"#{payload}\"> ]>\n#{m.source}"
                        m
                    end
                end
            end
        }
    end

Tips:
如果在Windows下启动Arachni,遇到提示:

find: ‘/C’: No such file or directory
find: ‘/I’: No such file or directory

这是由于,本地安装了Cygwin,导致Windows自带的find命令被覆盖。需要将Arachni\system文件夹下,setenv.bat文件第17行find,替换为"%windir%\system32\FIND.exe"。

4. 参考


微信公众号
作者
微信公众号