Xray 社区版 再学习记录

Posted by Mr.Be1ieVe on Friday, March 12, 2021

前言

以前也有用xray扫扫学校的漏洞,最近有在开发自己的扫描平台,估借此机会再学习。

官方文档

VulnType的复现在最后边。

三种基本模式

代理模式

初始配置证书部分看官方文档吧Orz, 代理模式进行扫描 - xray 安全评估工具文档

建议配合 SwitchyOmega 这个插件使用,可以随意切换代理服务器及端口。

config.yaml配置好

mitm:
  restriction: # 代理能够访问的资源限制, 以下各项为空表示不限制
    hostname_allowed: ['*.web-security-academy.net','192.168.3.188'] # 允许访问的 Hostname,支持格式如 t.com、*.t.com、1.1.1.1、1.1.1.1/24、1.1-4.1.1-8

然后游览页面即可,若没有config.yaml则先运行一次xray

基础爬虫模式

爬虫模式是模拟人工去点击网页的链接,然后去分析扫描,爬虫不需要人工的介入,访问速度要快很多,但是也有一些缺点

  • xray 的基础爬虫不能处理 js 渲染的页面

./xray webscan --basic-crawler http://testphp.vulnweb.com/ --html-output xray-crawler-testphp.html

配置Cookie

配置文件config.yaml里,http 配置部分的 headers 项:

添加一条Cookie,如

  headers:
    Cookie: key=value

例子

http:
  ...
 
  headers:
    Cookie: sysauth=xxxxx

服务扫描

目前主要是服务扫描相关的poc,目前只有一个 tomcat-cve-2020-1938 ajp 协议任意文件检测 poc。

target参数支持ip:port方式,和 xxx.file方式

./xray servicescan --target 127.0.0.1:8009
./xray servicescan --target xxx.file

xxx.file格式,一行一个ip:port目标,进行批量导入

127.0.0.1:8009
10.1.1.1:8099

可以使用如下不同的导出方式

NAME:
    servicescan - Run a service scan task

USAGE:
    servicescan [command options] [arguments...]

OPTIONS:
   --target value          指定单一目标,例子: host:8009
   --target-file value     从文件加载目标,一目标一行
   --json-output FILE      使用json格式导出xray结果
   --webhook-output value  用json格式post xray结果到某url
   --html-output FILE      用html格式导出报告

配置文件

HTTP 配置

http:
  proxy: ""                             # 漏洞扫描时使用的代理,如: http://127.0.0.1:8080。 如需设置多个代理,请使用 proxy_rule 或自行创建上层代理
  proxy_rule: []                        # 漏洞扫描使用多个代理的配置规则, 具体请参照文档

代理配置

支持 http, httpssocks5 三种格式,如:

http://127.0.0.1:1111
https://127.0.0.1:1111
socks5://127.0.0.1:1080

如果代理需要认证,可以使用下面的格式 http://user:password@127.0.0.1:1111

多代理配置

不同的域名使用不同的代理,设置多个代理切换等,可以通过 proxy_rule 字段来配置。==Proxy 配置将优先于本配置。==

proxy_rule:
  - match: "*host1"
    servers:
      - addr: "http://127.0.0.1:8001"
        weight: 1
      - addr: "http://127.0.0.1:8002"
        weight: 2
  - match: "*"
    servers:
      - addr: "http://127.0.0.1:8003"
        weight: 1
      - addr: "http://127.0.0.1:8004"
        weight: 5
  • match: 请求的 url 的主机名如果匹配,就使用本条规则。
    • 如果是 *,则代表可以匹配所有。所以一定要将 * 放在最后面,上面没有匹配到的域名都将使用这个配置。
    • 如果没有任何一条可以匹配,这个请求将不会使用代理。
  • addr: 代理服务器的地址,同 proxy 的配置。
  • weight: 代理服务器的权重,如果 servers 中配置了多个代理服务器,设置权重可以均衡负载,比如权重是 3:7,则代表每 10 个请求,有 3 个选择 server1,有 7 个选择 server2。要注意的是,这里是 round bin 算法,前 3 个一定发往 server1,后面 7 个一定发往 server2,然后继续循环,不是每个请求都是基于权重随机的。

插件配置

dirscan

  • depth 深度限制
  • dictionary 配置目录字典, 需要是绝对路径, 配置后将与内置字典合并

depth 是探测深度, 默认为 1, 即只在 URL 深度为 0, 和深度为 1 时运行该插件(前提是启用了该插件)

定义 http://example.com/,深度为 0,定义 http://example.com/a/, 深度为 1。 在这种配置下,dirscan 将对

http://example.com/http://example.com/a/ 做两次扫描,如果存在 http://example.com/a/example.zip 那么就能将其扫描出来。

sqldet

下面这个选项很危险,开启之后可以增加检测率,但是有破坏数据库数据的可能性,请务必了解工作原理之后再开启

  • dangerously_use_comment_in_sql 允许检查注入的时候使用注释

phantasm

xray 的 poc 框架,在其下运行着许多 yaml 和 go 写的 poc,用户可以通过该模块编写自己的 poc 并让 xray 加载,具体见 自定义POC语法

两个重点配置:

depth: 1                            # 与 dirscan 一样,不再赘述
exclude_poc: []                     # 排除哪些 poc, 支持 glob 语法, 如: /home/poc/*thinkphp* 或 poc-yaml-weblogic*
local_poc: []                       # 加载本地的 poc, 支持 glob 语法, 如: /home/poc/*

exclude_poc 用于去除加载哪些 poc。

plugins:
  ...
  phantasm:
    enabled: true
    exclude_poc:
    - poc-yaml-bad-poc
    - *bad-poc*

local_poc 是用于加载本地的 poc 的配置,最好指定绝对路径,且同样支持 glob 语法。

一个稍微复杂的情况是将这两个搭配起来使用,比如:

plugins:
  ...
  phantasm:
    enabled: true
    exclude_poc:
    - /home/poc/poc-fake-good-poc
    local_poc:
    - /home/poc/*good-poc*

上述配置的意思是加载 /home/poc/ 目录下所有符合 *good-poc* 这个pattern 的poc,同时去掉同样目录下的 poc-fake-good-poc

被动代理配置

这部分是代理模式的配置

代理启用密码保护

对应于 auth 中的配置。

支持给代理配置基础认证的密码,当设置好 auth 中的 usernamepassword 后,使用代理时浏览器会弹框要求输出用户名密码,输入成功后代理才可正常使用。

限制允许使用该代理的ip

allow_ip_range ,限制哪些ip可以使用该代理。支持单个 IP 和 CIDR 格式的地址,如:

allow_ip_range: ["127.0.0.1","192.168.1.1/24"]

留空则允许所有地址访问

限制扫描范围

restriction中,allowed即为允许访问的,disallowed即为不允许的。如

    hostname_allowed: []                # 允许访问的 Hostname,支持格式如 t.com、*.t.com、1.1.1.1、1.1.1.1/24、1.1-4.1.1-8
    hostname_disallowed:                # 不允许访问的 Hostname,支持格式如 t.com、*.t.com、1.1.1.1、1.1.1.1/24、1.1-4.1.1-8
    - '*google*'

代理请求头配置 proxy_header

proxy_header:
    via: "" # 如果不为空,proxy 将添加类似 Via: 1.1 $some-value-$random 的 http 头
    x_forwarded: false # 是否添加 X-Forwarded-{For,Host,Proto,Url} 四个 http 头

如果开启 proxy_header,代理会添加 via 头和 X-Forwarded-* 系列头。

比如 curl http://127.0.0.1:1234 -H "Via: test" -H "X-Forwarded-For: 1.2.3.4" -v,后端实际收到的请求将会是

GET / HTTP/1.1
Host: 127.0.0.1:1234
User-Agent: curl/7.54.0
Accept: */*
Via: test, 1.1 xray-1fe7f9e5241b2b150f32
X-Forwarded-For: 1.2.3.4, 127.0.0.1
X-Forwarded-Host: 127.0.0.1:1234
X-Forwarded-Proto: http
X-Forwarded-Url: http://127.0.0.1:1234/
Accept-Encoding: gzip

http的代理 upstream_proxy

该项配置仅对 http 代理本身生效,不对漏洞扫描发出的请求生效。漏扫的代理配置HTTP部分

基础爬虫配置

看配置就行

basic-crawler:
  max_depth: 0                          # 最大爬取深度, 0 为无限制
  max_count_of_links: 0                 # 本次爬取收集的最大链接数, 0 为无限制
  allow_visit_parent_path: false        # 是否允许爬取父目录, 如果扫描目标为 t.com/a/且该项为 false, 那么就不会爬取 t.com/ 这级的内容
  restriction:                          # 爬虫的允许爬取的资源限制, 为空表示不限制。爬虫会自动添加扫描目标到 Hostname_allowed。
    hostname_allowed: []                # 允许访问的 Hostname,支持格式如 t.com、*.t.com、1.1.1.1、1.1.1.1/24、1.1-4.1.1-8
    hostname_disallowed:                # 不允许访问的 Hostname,支持格式如 t.com、*.t.com、1.1.1.1、1.1.1.1/24、1.1-4.1.1-8
    - '*google*'
    - '*github*'
    - '*.gov.cn'
    - '*.edu.cn'
    - '*chaitin*'
    - '*.xray.cool'
    port_allowed: []                    # 允许访问的端口, 支持的格式如: 80、80-85
    port_disallowed: []                 # 不允许访问的端口, 支持的格式如: 80、80-85
    path_allowed: []                    # 允许访问的路径,支持的格式如: test、*test*
    path_disallowed: []                 # 不允许访问的路径, 支持的格式如: test、*test*
    query_key_allowed: []               # 允许访问的 Query Key,支持的格式如: test、*test*
    query_key_disallowed: []            # 不允许访问的 Query Key, 支持的格式如: test、*test*
    fragment_allowed: []                # 允许访问的 Fragment, 支持的格式如: test、*test*
    fragment_disallowed: []             # 不允许访问的 Fragment, 支持的格式如: test、*test*
    post_key_allowed: []                # 允许访问的 Post Body 中的参数, 支持的格式如: test、*test*
    post_key_disallowed: []             # 不允许访问的 Post Body 中的参数, 支持的格式如: test、*test*
  basic_auth:                           # 基础认证信息
    username: ""
    password: ""

反连平台

反连平台默认不启用,因为这里面有些配置没有办法自动化,必须由人工配置完成才可使用。需要反连平台才可以检测出来的漏洞包括但不限于:

  • ssrf
  • fastjson
  • s2-052
  • xxe 盲打
  • 所有依赖反连平台的 poc
reverse:
  db_file_path: ""                      # 反连平台数据库文件位置, 这是一个 KV 数据库
  token: ""                             # 反连平台认证的 Token, 独立部署时不能为空
  http:
    enabled: false
    listen_ip: 0.0.0.0 
    listen_port: ""
    ip_header: ""                       # 在哪个 http header 中取 ip,为空代表从 REMOTE_ADDR 中取
  dns:
    enabled: false
    listen_ip: 0.0.0.0 
    domain: ""                          # DNS 域名配置
    is_domain_name_server: false        # 是否修改了域名的 ns 为反连平台,如果是,那 nslookup 等就不需要指定 dns 了
    resolve:                            # DNS 静态解析规则
    - type: A                           # A, AAAA, TXT 三种
      record: localhost
      value: 127.0.0.1
      ttl: 60
  client:
    remote_server: false                # 是否是独立的远程 server,如果是要在下面配置好远程的服务端地址
    http_base_url: ""                   # 默认将根据 ListenIP 和 ListenPort 生成,该地址是存在漏洞的目标反连回来的地址, 当反连平台前面有反代、绑定域名、端口映射时需要自行配置
    dns_server_ip: ""                   # 和 http_base_url 类似,实际用来访问 dns 服务器的地址

例子

xray和目标可以用ip双向互联

假设 192.168.1.2 是 xray 所在机器的地址

reverse:
  http:
    enabled: true
    listen_ip: 192.168.1.2

这里的 192.168.1.2 是 xray 所在的机器的地址, 也是扫描目标反连时所使用的地址。也就是,目标存在漏洞的时候,会访问 http://192.168.1.2/xxxx 携带漏洞结果和数据

xray 和目标可以使用 ip 双向互联,但 listen 的地址和目标反连访问的地址不一样

这种情况经常出现在云主机中,比如腾讯云的 linux 机器网卡地址时 10.xxx,但实际公网假设是 221.xxx

reverse:
  http:
    enabled: true
    listen_ip: 0.0.0.0
  client:
    http_base_url: "http://221.xxx:${port}" 

${port} 是一个预定义的宏,在运行时将被替换为实际监听的端口, 当然你也可以使用这种方式固定监听的端口:

reverse:
  http:
    enabled: true
    listen_ip: 0.0.0.0
    listen_port: 8888
  client:
    http_base_url: "http://221.xxx:8888" 
单独布置反连平台

xray 自带了一个 reverse 命令,可以启动反连平台作为一个远程服务运行,给多个客户端使用。

在启动之前,需要配置好配置文件的 db_file_pathtoken,前者用于存储数据,后者用于服务端和客户端通信的秘钥。

一个示例的服务端配置是:

reverse:
  db_file_path: "reverse.db"
  token: "please_change_me_to_a_new_token"
  http:
    listen_ip: 0.0.0.0
    listen_port: "80"

与之对应的客户端配置是:

reverse:
  token: "please_change_me_to_a_new_token"
  client:
    remote_server: true
    http_base_url: "http://YOUR_REVERSE_SERVER_IP:80"

需要客户端和服务端版本相同

管理界面

反连平台贴心的内置了一个管理界面,可以访问反连平台 http 地址,url 为 /cland/

功能包括

  • 生成自定义 url,并记录访问记录
  • 生成自定义域名,并记录解析记录
  • 使用平台预制 payload,记录抓取的数据

mac 下的 docker 由于网络环境特殊,其实无法双向互联,建议使用远程部署的模式或使用 host.docker.internal 作为 http_base_url 的 host

子域名配置

subdomain:
  max_parallel: 30                      # 子域名探测的并发度
  allow_recursion: false                # 是否允许递归探测, 开启后,扫描完一级域名后,会自动将一级的每个域名作为新的目标
  max_recursion_depth: 3                # 最大允许的递归深度, 3 表示 3 级子域名 仅当 allow_recursion 开启时才有意义
  web_only: false                       # 结果中仅显示有 web 应用的, 没有 web 应用的将被丢弃
  ip_only: false                        # 结果中仅展示解析出 IP 的,没有解析成功的将被丢弃
  servers:                              # 子域名扫描过程中使用的 DNS Server
  - 8.8.8.8
  - 8.8.4.4
  - 223.5.5.5
  - 223.6.6.6
  - 114.114.114.114
  sources:
    brute:
      enabled: true
      main_dict: ""                     # 一级大字典路径,为空将使用内置的 TOP 30000 字典
      sub_dict: ""                      # 其他级小字典路径,为空将使用内置过的 TOP 100 字典
    httpfinder:
      enabled: true                     # 使用 http 的一些方式来抓取子域名,包括 js, 配置文件,http header 等等
    dnsfinder:
      enabled: true                     # 使用 dns 的一些错误配置来找寻子域名,如区域传送(zone transfer)
    certspotter:                        # 下面的通过 api 获取的了
      enabled: true
    crt:
      enabled: true
    hackertarget:
      enabled: true
    qianxun:
      enabled: true
    rapiddns:
      enabled: true
    sublist3r:
      enabled: true
    threatminer:
      enabled: true
    virusTotal:
      enabled: true

检查更新配置

添加如下即可禁用更新检查

update:
  check: false

并发配置

parallel: 30 # 漏洞探测的 worker 数量,可以简单理解为同时有 30 个 POC 在运行

默认基本足够

高级用法

优化扫描速度

在 xray 的默认配置中,影响扫描速度的主要有两个参数

  • max_qps 每秒最大请求数,默认值为 500
  • max_parallel 插件调度并发数,默认值为 30

即使到了 2000 qps,也还没有达到配置的检测最大并发,所以 max_parallel 一般情况下不需要修改。 max_qps 在非测试场景下,也很难打满,一般情况下只会改低,防止影响业务,而不需要再调高。

总结

限制 xray 扫描速度的主要原因为

  • xray 与被扫描目标之间的网络情况
  • 被扫描目标的性能;否则网络再好,扫描速度也无法提升

这两点可以从 latencyfailedRatio 字段评估。latency 比较大,超过 100ms,说明网络比较慢,或者被扫描的目标响应比较慢。failedRatio 比较大,说明网络比较差,或者被扫描目标负载高而失去了响应等。

在网络情况稳定的情况下,可以考虑

  • 降低 max_qps,防止业务方负载过高
  • 关闭特定模块和禁用部分 poc,减少请求量
  • 不要性能比较差的代理作为 xray 的扫描代理,比如 burp 的代理通过请求的速度基本只有几十 qps,这样整个扫描的瓶颈就在 burp 上了。

自定义POC语法和编写高质量POC

官方文档

webhook

--webhook-output

统一的格式为:

{
    "type": "xxx",
    "data": {}
}

type 的 enum 为 :

  • “web_statistic” web 扫描统计信息
  • “web_vuln” web 漏洞
  • “host_vuln” 服务漏洞
  • “subdomain” 子域名

漏洞信息

{
  "type": "web_vuln",
  "data": {
    "create_time": 1604736253090,
    "detail": {
      "addr": "http://127.0.0.1:9000/xss/example1.php?name=hacker",
      "extra": {
        "param": {
          "key": "name",
          "position": "query",
          "value": "pkbnekwkjhwzabxnfjwh"
        }
      },
      "payload": "<sCrIpT>alert(1)</ScRiPt>",
      "snapshot": [
        [
          "GET /xxx",
          "HTTP/1.1 200 OK"
        ]
      ]
    },
    "plugin": "xss/reflected/default",
    "target": {
      "params": [],
      "url": "http://127.0.0.1:9000/xss/example1.php"
    }
  }
}
  • type 是 web_vulnhost_vuln
  • snapshot 是漏洞探测的请求流,定义是 [][2]string, 即每一组有两部分,对应于请求和响应。对于 sql 注入这种就会有多组请求响应。
  • target 实际意义不大, 主要是 detail 中的部分, detail 的定义为:
type VulnDetail struct {
    Addr     string                 `json:"addr" yaml:"addr"`
    Payload  string                 `json:"payload" yaml:"payload"`
    SnapShot []interface{}          `json:"snapshot" yaml:"snapshot"`
    Extra    map[string]interface{} `json:"extra" yaml:"extra"`
}

web统计信息

{
  "type": "web_statistic",
  "data": {
    "num_found_urls": 1,
    "num_scanned_urls": 1,
    "num_sent_http_requests": 3,
    "average_response_time": 0,
    "ratio_failed_http_requests": 0,
  }
}
  • num_found_urls 发现的扫描目标数量
  • num_scanned_urls 已扫描完成的扫描目标数量
  • num_sent_http_requests 已经发出的漏洞探测请求数量
  • average_response_time 近 30s 请求平均响应时间
  • ratio_failed_http_requests 近 30s 请求失败率

这些信息除了表面意思,还有其他隐藏含义:

  • pending 的数量等于 num_found_urls - num_scanned_urls, 如果 pending = 0,那么就意味着扫描完成了
  • average_response_time 比较高 (>200ms) 意味着网络延迟比较大
  • ratio_failed_http_requests 比较高(>5%) 意味着可能有 waf, 反正就是很多请求失败了

子域名消息格式

{
  "type": "subdomain",
  "data": {
    "cname": [],
    "domain": "example.com",
    "extra": "",
    "ip": [
      {
        "asn": "EDGECAST (ASN15133)",
        "country": "北美洲 美国",
        "ip": "93.184.216.34"
      },
      {
        "asn": "EDGECAST (ASN15133)",
        "country": "北美洲 美国",
        "ip": "2606:2800:220:1:248:1893:25c8:1946"
      }
    ],
    "parent": "example.com",
    "verbose_name": "initial",
    "web": [
      {
        "link": "http://example.com/",
        "server": "ECS (sjc/4FB8)",
        "status": 200,
        "title": "Example Domain"
      },
      {
        "link": "https://example.com/",
        "server": "ECS (sjc/4E8D)",
        "status": 200,
        "title": "Example Domain"
      }
    ]
  }
}

VulnType大类复现

Server-error

使用bp的Repeater,设置目标复制Request即可。由于是概率引发error,多次刷新才行。

crossdomain

config/ide

其中,FileEditorManager包含文件名及相对路径

<component name="FileEditorManager">
 ...
 <file leaf-file-name="cart.php" pinned="false" current="true" current-in-tab="true">
  <entry file="file://$PROJECT_DIR$/cart.php">

Git.Settings包含Git的一些设置

<component name="Git.Settings">
   <option name="CHECKOUT_INCLUDE_TAGS" value="false"/>
   <option name="UPDATE_CHANGES_POLICY" value="STASH"/>
   <option name="LINE_SEPARATORS_CONVERSION" value="ASK"/>
</component>

ide文件历史记录

<component name="IdeDocumentHistory">
 <option name="changedFiles">
  <list>
   <option value="$PROJECT_DIR$/.htaccess"/>
   <option value="$PROJECT_DIR$/cart.php"/>
  </list>
 </option>
</component>

修改历史记录

<component name="ChangeListManager">
 <list default="true" id="190e5f86-a42c-4cc9-a03a-80ac0d47d6d2" name="Default" comment=""/>
 <ignored path="acuart.iws"/>
 <ignored path=".idea/workspace.xml"/>
 <option name="TRACKING_ENABLED" value="true"/>
 <option name="SHOW_DIALOG" value="false"/>
 <option name="HIGHLIGHT_CONFLICTS" value="true"/>
 <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false"/>
 <option name="LAST_RESOLUTION" value="IGNORE"/>
</component>

<component name="editorHistoryManager">
 <entry file="file://$PROJECT_DIR$/.htaccess">
  <provider selected="true" editor-type-id="text-editor">
   <state line="6" column="0" selection-start="213" selection-end="222" vertical-scroll-proportion="0.124847">
 ...
 <entry file="file://$PROJECT_DIR$/cart.php">
  <provider selected="true" editor-type-id="text-editor">
   <state line="60" column="26" selection-start="2336" selection-end="2336" vertical-scroll-proportion="-0.41645244">

 ...
</component>

poc-yaml-phpstudy-nginx-wrong-resolve

请求了

94561621.php
index.php
index.php/.php
index.php/.xxx

只有index.phpindex.php/.php请求成功了,并且都返回的是index.php的页面。但由于网页使用相对路径加载图片,所以在index.php/.php时图片都无法正常加载。

「真诚赞赏,手留余香」

Mr.Be1ieVe's Treasure

真诚赞赏,手留余香

使用微信扫描二维码完成支付