利用了几种方式,发现都不行
1是修改mime类型,2是修改php标签为js标签,3是修改文件后缀
在试试用配置文件来上传
发现上传.user.ini文件成功
发现上传成功
ls /
看标签是一个cve漏洞
CVE-2022-1292的分析 - 先知社区
扫后台扫出来一个网页
[CISCN 2022 初赛]online_crt__rev1ve的博客-CSDN博客
给了源码附件
app.py源码
import datetime import json import os import socket import uuid from cryptography import x509 from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.x509.oid import NameOID from flask import Flask from flask import render_template from flask import request app = Flask(__name__) app.config['SECRET_KEY'] = os.urandom(16) def get_crt(Country, Province, City, OrganizationalName, CommonName, EmailAddress): root_key = rsa.generate_private_key( public_exponent=65537, key_size=2048, backend=default_backend() ) subject = issuer = x509.Name([ x509.NameAttribute(NameOID.COUNTRY_NAME, Country), x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, Province), x509.NameAttribute(NameOID.LOCALITY_NAME, City), x509.NameAttribute(NameOID.ORGANIZATION_NAME, OrganizationalName), x509.NameAttribute(NameOID.COMMON_NAME, CommonName), x509.NameAttribute(NameOID.EMAIL_ADDRESS, EmailAddress), ]) root_cert = x509.CertificateBuilder().subject_name( subject ).issuer_name( issuer ).public_key( root_key.public_key() ).serial_number( x509.random_serial_number() ).not_valid_before( datetime.datetime.utcnow() ).not_valid_after( datetime.datetime.utcnow() + datetime.timedelta(days=3650) ).sign(root_key, hashes.SHA256(), default_backend()) crt_name = "static/crt/" + str(uuid.uuid4()) + ".crt" with open(crt_name, "wb") as f: f.write(root_cert.public_bytes(serialization.Encoding.PEM)) return crt_name @app.route('/', methods=['GET', 'POST']) def index(): return render_template("index.html") @app.route(' ', methods=['GET', 'POST']) def upload(): Country = request.form.get("Country", "CN") Province = request.form.get("Province", "a") City = request.form.get("City", "a") OrganizationalName = request.form.get("OrganizationalName", "a") CommonName = request.form.get("CommonName", "a") EmailAddress = request.form.get("EmailAddress", "a") return get_crt(Country, Province, City, OrganizationalName, CommonName, EmailAddress) @app.route('/createlink', methods=['GET']) def info(): json_data = {"info": os.popen("c_rehash static/crt/ && ls static/crt/").read()} return json.dumps(json_data) @app.route('/proxy', methods=['GET']) def proxy(): uri = request.form.get("uri", "/") client = socket.socket() client.connect(('localhost', 8887)) msg = f'''GET {uri} HTTP/1.1 Host: test_api_host User-Agent: Guest Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Connection: close ''' client.send(msg.encode()) data = client.recv(2048) client.close() return data.decode() app.run(host="0.0.0.0", port=8888)
gpt的解释
此代码似乎是定义 Flask Web 应用程序的 Python 脚本。让我们来看看主要组件及其功能:
导入语句:该脚本导入其功能所需的多个模块和包,包括 、 、 、 、 和 。
datetime
json
os
socket
uuid
cryptography
Flask
Flask 应用程序设置:使用 创建 Flask 应用程序的实例,并使用 设置应用程序的密钥。
Flask(__name__)
app.config['SECRET_KEY']
get_crt
函数:此函数根据提供的输入参数生成 X.509 证书(SSL/TLS 证书)。它使用该库生成私钥、生成证书并将其保存到文件中。返回生成的证书的文件路径。cryptography
路由定义:
'/'
:此路由与应用程序的根 URL 相关联。它使用 呈现名为“index.html”的 HTML 模板。render_template
'/getcrt'
:此路由与 URL 路径“/getcrt”相关联,并接受 GET 和 POST 请求。它从请求(国家/地区、省、市、组织名称、公用名、电子邮件地址)中检索表单数据,并使用提供的数据调用函数。然后,将 的返回值作为响应返回。get_crt
get_crt
'/createlink'
:此路由与 URL 路径“/createlink”相关联,并接受 GET 请求。它会生成一个 JSON 响应,其中包含有关“static/crt/”目录中生成的证书文件的信息。'/proxy'
:此路由与 URL 路径“/proxy”相关联,并接受 GET 请求。它从请求中检索 URI 参数,并在端口 8887 上与 localhost 服务器建立套接字连接。它向指定的 URI 发送 HTTP GET 请求,并将接收到的数据作为响应返回。
app.run()
:此方法启动 Flask 开发服务器,允许应用程序侦听所有网络接口 (host=“0.0.0.0”) 和端口 8888 上的传入请求。总之,此脚本定义了一个 Flask Web 应用程序,该应用程序提供用于生成 X.509 证书、呈现 HTML 模板以及充当代理服务器以向指定 URI 发出 HTTP 请求的功能。
分析一下,采用的是flask模板,总计4个路由
/根目录返回一个index.htmld的界面,也就是我们开题看到的界面
/getcrt路由配合getcrt函数用来生成X509证书(了解一下就行,它会根据我们提交的信息创建一个.crt的证书文件,存入static/crt/目录下)
/createlink路由,用来建立一个与我们刚才生成的.crt证书的链接,去访问它的内容
/proxy路由,用来建立一个内网 8887端口的连接,与go源码部分进行对接,并将发送一段请求信息给go源码的部分进行处理
go源码
package main
import (
"github.com/gin-gonic/gin"
"os"
"strings"
)
func admin(c *gin.Context) {
staticPath := "/app/static/crt/"
oldname := c.DefaultQuery("oldname", "")
newname := c.DefaultQuery("newname", "")
if oldname == "" || newname == "" || strings.Contains(oldname, "..") || strings.Contains(newname, "..") {
c.String(500, "error")
return
}
if c.Request.URL.RawPath != "" && c.Request.Host == "admin" {
err := os.Rename(staticPath+oldname, staticPath+newname)
if err != nil {
return
}
c.String(200, newname)
return
}
c.String(200, "no")
}
func index(c *gin.Context) {
c.String(200, "hello world")
}
func main() {
router := gin.Default()
router.GET("/", index)
router.GET("/admin/rename", admin)
if err := router.Run(":8887"); err != nil {
panic(err)
}
}
这一段代码比较重要的就是只要绕过if语句就可以修改我们指定的.crt后缀的证书文件名
第一个判断直接将路径中的/替换为%2f即可通过,第二个要求HOST为admin
本题漏洞
出在/createlink这个路由下的c_rehash指令,c_rehash是openssl中的一个用perl编写的脚本工具,用于批量创建证书等文件 hash命名的符号链接,是当用户可控文件名时的命令注入
在openssl
中的c_rehash
存在命令注入, 允许以c_reash
脚本的权限执行命令
先访问/getcrt
生成一个证书 9eaf2906-aea8-4bf7-9d5f-14c266c18856.crt
然后通过访问 /proxy路由来打通与内容 go源码业务部分的连接,修改我们的证书文件名,并加上我们的命令执行RCE
payload生效的内容是在同目录下生成一个flag.txt文件并向flag.txt文件写入我们 cat /*的结果
uri是被直接拼接进去的,因此存在CRLF漏洞,我们就可以篡改HOST为admin从而满足go server修改文件名的要求。
CRLF注入漏洞、URL重定向、资源处理拒绝服务详细介绍(附实例)_crlf漏洞_ranzi.的博客-CSDN博客
CRLF注入漏洞(响应截断)攻击实战_crlf漏洞_归去来兮-zangcc的博客-CSDN博客
访问/proxy
,同时抓包修改请求方式为GET以及在uri参数后回车两次
抓get包,然后利用bp改成post,最后再手改为get请求,发送
访问/createlink路由,执行 c_rehash命令,进而触发命令执行,使payload生效
payload生效的内容是在同目录下生成一个flag.txt文件并向flag.txt文件写入我们 cat /*的结果
访问同目录static/crt/flag.txt 拿到flag信息
题目提示是无回显的rce,之前做就是用tee命令写入文件
?cmd=ls / | tee 1.txt
?cmd=cat /fl\ag | tee 1.txt
代码审计
定义了公共变量a,b,file
a不等于b但是a,b的MD5和sha1值相等;如果为true,那么将file值传入date函数并赋值给变量content;使用 uniqid() 生成一个唯一的标识符,并将其与 .txt 扩展名拼接为文件名,赋值给变量 $uuid;最后就是进行正则匹配,读取文件
preg_replace('/((\s)*(\n)+(\s)*)/i', '', $data)
:此函数对字符串执行正则表达式替换。使用的正则表达式模式是 ,它匹配空格字符和换行符的任意组合。替换参数是一个空字符串,这意味着匹配的模式将被替换为任何内容(即删除)。$data
'/((\s)*(\n)+(\s)*)/i'
''
class date{
public $a;
public $b;
public $file;
}$A=new date();
$A->a=1;
$A->b='1';
$A->file='/f\l\a\g';
echo base64_encode(serialize($A));
?>
得到flag
点flag.txtc出现了flag的文件
welcome.txt
查了资料之后才想起来
Tornado模板注入 - 先知社区
tornado render是python中的一个渲染函数,也就是一种模板,通过调用的参数不同,生成不同的网页,如果用户对render内容可控,不仅可以注入XSS代码,而且还可以通过{{}}进行传递变量和执行简单的表达式。
url形如 :http://35848a5d-982a-45c9-a772-1b59e317746f.node3.buuoj.cn/file?filename=/flag.txt&filehash=32c8987c242bb2c5f0b0da175550dab6
根据提示应该payload应该是file?filename=/fllllllllllllag&filehash=md5(cookie_secret+md5(filename)),因此现在需要找到cookie_secret
将filename替换为/fllllllllllllag 后发现报错,修改/error?msg=Error错误信息中的msg页面也跟着变
根据render提示应该是模板注入,但存在过滤,ORZ
这里用到的是handler.setting对象
handler 指向RequestHandler
而RequestHandler.settings又指向self.application.settings
所有handler.settings就指向RequestHandler.application.settings
传递error?msg={{ handler.settings }}得到:
得到secret后,就是计算访问flag文件的hash,获取flag
cookie_secret=eb535b6a-633c-4b3d-a321-8d1637396e1d
脚本:
import hashlib s1=(hashlib.md5("/fllllllllllllag".encode())).hexdigest() print((hashlib.md5(("eb535b6a-633c-4b3d-a321-8d1637396e1d"+s1).encode())).hexdigest())
得到hash值
访问flag文件,得到flag